Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/link frontend #45

Merged
merged 3 commits into from
Sep 27, 2024
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
125 changes: 125 additions & 0 deletions app/dashboard/components/uploader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, { useState, useEffect } from "react";
import { AlertCircle, X, CloudUpload } from "lucide-react";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";

interface UploaderProps {
className?: string; // Optional className prop
}

const Uploader: React.FC<UploaderProps> = ({ className }) => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [showAlert, setShowAlert] = useState(false);
const allowedFileTypes = [".json", ".txt"]; // Allowed file extensions
const maxFileSize = 100 * 1024 * 1024; // 100MB

useEffect(() => {
let timer: NodeJS.Timeout;
if (showAlert) {
timer = setTimeout(() => {
setShowAlert(false);
}, 2000);
}
return () => clearTimeout(timer);
}, [showAlert]);

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file && allowedFileTypes.includes(file.name.split(".").pop() || "")) {
setSelectedFile(file);
setShowAlert(false);
} else {
setShowAlert(true);
}
};

const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
};

const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const file = event.dataTransfer.files?.[0];
if (
file &&
allowedFileTypes.includes(file.name.split(".").pop() || "") &&
file.size <= maxFileSize
) {
setSelectedFile(file);
setShowAlert(false);
} else {
setShowAlert(true);
}
};

return (
<div className={className}>
{showAlert && (
<div className="fixed top-2 right-2 z-50">
<Alert
variant="destructive"
className="flex justify-between items-center"
>
<div className="flex items-center">
<AlertCircle className="h-4 w-4 mr-2" />
<div>
<AlertTitle>Error</AlertTitle>
<AlertDescription>
Invalid file type or file size exceeds the limit.
</AlertDescription>
</div>
</div>
<button
onClick={() => setShowAlert(false)}
className="ml-4 transition transform hover:scale-110 active:scale-90"
>
<X className="h-4 w-4" />
</button>
</Alert>
</div>
)}

<h2 className="text-2xl font-bold">Upload your data</h2>
<p className="mb-4 text-sm text-gray-500">
Use the dropzone below to upload your file.
</p>

<div
className="border-2 border-gray-300 p-8 rounded-lg bg-gray-50 cursor-pointer w-full relative text-center flex flex-grow justify-center items-center "
onDragOver={handleDragOver}
onDrop={handleDrop}
>
<div className="">
<CloudUpload className="mx-auto mb-2" size={80} />
<p className="text-gray-600">
Drag or <span className="text-blue-600 cursor-pointer">Browse</span>{" "}
your files
</p>
<p className="text-gray-400 mb-2">or</p>
<a href="#" className="text-blue-600">
Use a sample file
</a>

<input
type="file"
accept=".json, .txt"
onChange={handleFileChange}
className="top-0 left-0 w-full h-full opacity-0 cursor-pointer"
/>
<p className="text-sm text-gray-400 mt-4 absolute bottom-2 left-2">
Supported files: .json, .txt | Upload limit: 100MB
</p>
</div>
</div>

{selectedFile && (
<div className="mt-4">
<p>Selected file: {selectedFile.name}</p>
<p>File type: {selectedFile.type}</p>
</div>
)}
</div>
);
};

export default Uploader;
45 changes: 27 additions & 18 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use client";

import Image from "next/image";
import Uploader from "./components/uploader";
import React, { useEffect } from "react";

const Dashboard: React.FC = () => {
Expand All @@ -17,24 +19,31 @@ const Dashboard: React.FC = () => {
}, []);

return (
<div className="flex h-screen">
{/* Left Tool Bar */}
<aside className="w-1/8 bg-gray-800 text-white p-6">
<h2 className="text-xl font-semibold mb-6">AuditVisualizer</h2>
<ul className="space-y-4">
<li className="hover:bg-gray-700 p-2 rounded">Main</li>
<li className="hover:bg-gray-700 p-2 rounded">Func_1</li>
<li className="hover:bg-gray-700 p-2 rounded">Func_2</li>
<li className="hover:bg-gray-700 p-2 rounded">Setting</li>
</ul>
</aside>

{/* Main Work Space */}
<main className="flex-1 bg-gray-100 p-8">
<h1 className="text-3xl font-bold mb-6">Dashboard</h1>
<div className="bg-white shadow-md rounded-lg p-6">
<p>TODO</p>
{/* Add more Stuff here*/}
<div className="flex flex-col h-screen">
<main className="flex flex-col bg-gray-100 p-8 h-screen">
<div className="flex items-center mb-6">
<Image src="/Logo.png" alt="Logo" width={80} height={80} />
<h1 className="text-3xl font-bold">AuditVisualiser</h1>
</div>
<div className="bg-white shadow-md rounded-lg p-6 flex flex-col flex-grow">
<h2 className="text-3xl font-bold text-left">
Show the effect of assertions
</h2>
<p className="text-gray-500 text-left">
Evaluate any audit result that is formatted as a JSON file.
</p>
<Uploader className="flex flex-col flex-grow text-left p-4" />
<p className="bg-white text-gray-400 text-center">
By sharing your files or using our service, you agree to our&nbsp;
<span className="underline hover:text-gray-800">
Terms of Service
</span>
&nbsp;and&nbsp;
<span className="underline hover:text-gray-800">
Privacy Policy
</span>
.
</p>
</div>
</main>
</div>
Expand Down
59 changes: 59 additions & 0 deletions components/ui/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";

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

const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
);

const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
));
Alert.displayName = "Alert";

const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
));
AlertTitle.displayName = "AlertTitle";

const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
));
AlertDescription.displayName = "AlertDescription";

export { Alert, AlertTitle, AlertDescription };
50 changes: 50 additions & 0 deletions components/ui/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";

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

const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className,
)}
{...props}
/>
));
Avatar.displayName = AvatarPrimitive.Root.displayName;

const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;

const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className,
)}
{...props}
/>
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;

export { Avatar, AvatarImage, AvatarFallback };
30 changes: 30 additions & 0 deletions components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";

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

const TooltipProvider = TooltipPrimitive.Provider;

const Tooltip = TooltipPrimitive.Root;

const TooltipTrigger = TooltipPrimitive.Trigger;

const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
Loading