Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 111 additions & 86 deletions backend/web/web-app-debug.html

Large diffs are not rendered by default.

197 changes: 111 additions & 86 deletions backend/web/web-app.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
Expand Down
71 changes: 71 additions & 0 deletions web-app/src/components/WindowsDownloadModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
DialogFooter,
DialogClose,
} from "@/components/ui/dialog";

type Props = {
onDownload: () => void;
disabled?: boolean;
triggerClassName?: string;
};
// allows download and modal popup at same time
export function WindowsDownloadModal({ onDownload, disabled, triggerClassName }: Props) {
return (
<Dialog>
<DialogTrigger asChild>
{/* Dashboard Button */}
<Button variant="outline" className={triggerClassName} disabled={disabled} onClick={onDownload}>
Download for Windows
</Button>
</DialogTrigger>

{/* Modal */}

<DialogContent className="sm:max-w-[660px]">
<DialogHeader>
<DialogTitle>Download started</DialogTitle>
<DialogDescription>
The Hopp Installer is downloading in the background. If the SmartScreen warning appears, follow the steps
below.
</DialogDescription>
</DialogHeader>

<div className="grid gap-4 py-4">
<div className="flex justify-center">
<video
src="https://dlh49gjxx49i3.cloudfront.net/web-app/wmv3.mp4"
className="w-[80%] rounded-lg border border-gray-400 shadow-sm object-contain"
autoPlay
loop
muted
playsInline
/>
</div>

<ol className="list-decimal space-y-1 pl-5 text-sm">
<li>
When “Windows protected your PC” appears, click <b>More info</b>.
</li>
<li>
Click <b>Run anyway</b>.
</li>
<li>Follow additional prompts to start the installation.</li>
</ol>
</div>

<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Close</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
95 changes: 95 additions & 0 deletions web-app/src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";

import { cn } from "@/lib/utils";

const Dialog = DialogPrimitive.Root;

const DialogTrigger = DialogPrimitive.Trigger;

const DialogPortal = DialogPrimitive.Portal;

const DialogClose = DialogPrimitive.Close;

const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;

const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
);
DialogHeader.displayName = "DialogHeader";

const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
);
DialogFooter.displayName = "DialogFooter";

const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;

const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;

export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
};
13 changes: 6 additions & 7 deletions web-app/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { SignInSuccessModal } from "@/components/SignInSuccessModal";
import { usePostHog } from "posthog-js/react";
import { queryClient } from "@/App";
import { WindowsDownloadModal } from "@/components/WindowsDownloadModal";

// Create email validation schema using zod
const emailSchema = z.string().email("Invalid email format");
Expand Down Expand Up @@ -509,14 +510,12 @@ export function Dashboard() {
<span className="font-normal">Windows</span>
<span className="muted">Windows 7 or later</span>
</div>
<Button
variant="outline"
className="ml-auto"
onClick={() => downloadFile("WINDOWS")}

<WindowsDownloadModal
onDownload={() => downloadFile("WINDOWS")}
disabled={!latestRelease}
>
Download for Windows
</Button>
triggerClassName="ml-auto"
/>
</div>
<div className="flex flex-row items-center justify-center gap-6">
<VscTerminalLinux className="size-4 text-slate-600" />
Expand Down
Loading