From cf857bc8af5ac3725f3bdb40dcdc80752595652f Mon Sep 17 00:00:00 2001 From: omagdy7 Date: Wed, 15 May 2024 23:23:57 +0300 Subject: Final version of backend and frontend --- frontend/src/App.tsx | 2 + frontend/src/components/Dashboard.tsx | 186 +++++++++++++++++++++++++++++ frontend/src/components/ImageProcessor.tsx | 67 +++++++---- frontend/src/components/ImageSideBar.tsx | 24 ++++ frontend/src/components/ui/button.tsx | 56 +++++++++ frontend/src/components/ui/input.tsx | 25 ++++ frontend/src/components/ui/select.tsx | 158 ++++++++++++++++++++++++ frontend/src/index.css | 77 +++++++++++- frontend/src/lib/utils.ts | 6 + 9 files changed, 576 insertions(+), 25 deletions(-) create mode 100644 frontend/src/components/Dashboard.tsx create mode 100644 frontend/src/components/ImageSideBar.tsx create mode 100644 frontend/src/components/ui/button.tsx create mode 100644 frontend/src/components/ui/input.tsx create mode 100644 frontend/src/components/ui/select.tsx create mode 100644 frontend/src/lib/utils.ts (limited to 'frontend/src') diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 370832d..cebfe44 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,9 +1,11 @@ import './App.css' +import Dashboard from './components/Dashboard' import ImageProcessor from './components/ImageProcessor' function App() { return ( + // ) } diff --git a/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx new file mode 100644 index 0000000..0834bee --- /dev/null +++ b/frontend/src/components/Dashboard.tsx @@ -0,0 +1,186 @@ +import { useEffect, useState } from 'react'; +import { EKSClient, DescribeClusterCommand } from '@aws-sdk/client-eks'; +import axios from 'axios'; + +// Configure AWS SDK v3 +const region = 'us-east-1'; // Change to your region + + +// Create an Axios instance +const axiosInstance = axios.create(); + +// Add a request interceptor +axiosInstance.interceptors.request.use(config => { + config.headers['Access-Control-Allow-Origin'] = '*'; + return config; +}, error => { + return Promise.reject(error); +}); + + + +const eksClient = new EKSClient({ + region, + credentials: { + accessKeyId: "AKIA3X4DCJJWHYF5M42F", + secretAccessKey: "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", + }, +}); + +const Header = () => { + return ( +
+

Kubernetes Dashboard

+
+ ); +}; + +const Card = ({ title, children, className = "" }) => { + return ( +
+

{title}

+ {children} +
+ ); +}; + +const StatusItem = ({ value, label }) => { + return ( +
+

{value}

+

{label}

+
+ ); +}; + +const StatusTable = ({ headers, rows }) => { + return ( +
+ + + + {headers.map((header, index) => ( + + ))} + + + + {rows.map((row, rowIndex) => ( + + {row.map((cell, cellIndex) => ( + + ))} + + ))} + +
{header}
= headers.length - 2 ? 'text-right' : 'text-left'} dark:text-white`}> + {cell} +
+
+ ); +}; + +const Dashboard = ({ clusterName }) => { + const [clusterOverview, setClusterOverview] = useState({}); + const [nodes, setNodes] = useState([]); + const [pods, setPods] = useState([]); + + useEffect(() => { + const fetchClusterData = async () => { + try { + const clusterData = await eksClient.send(new DescribeClusterCommand({ name: clusterName })); + const endpoint = clusterData.cluster.endpoint; + + console.log(endpoint) + + const nodeCount = await getNodeCount(endpoint); + const podCount = await getPodCount(endpoint); + const serviceCount = await getServiceCount(endpoint); + + setClusterOverview({ + nodes: nodeCount, + pods: podCount, + services: serviceCount, + }); + + const nodesData = await getNodeStatuses(endpoint); + setNodes(nodesData); + + const podsData = await getPodStatuses(endpoint); + setPods(podsData); + + } catch (error) { + console.error('Error fetching data from AWS:', error); + } + }; + + fetchClusterData(); + }, [clusterName]); + + const getNodeCount = async (endpoint) => { + const response = await axiosInstance.get(`${endpoint}/api/v1/nodes`); + return response.data.items.length; + }; + + const getPodCount = async (endpoint) => { + const response = await axiosInstance.get(`${endpoint}/api/v1/pods`); + return response.data.items.length; + }; + + const getServiceCount = async (endpoint) => { + const response = await axiosInstance.get(`${endpoint}/api/v1/services`); + return response.data.items.length; + }; + + const getNodeStatuses = async (endpoint) => { + const response = await axiosInstance.get(`${endpoint}/api/v1/nodes`); + return response.data.items.map(node => ({ + name: node.metadata.name, + status: node.status.conditions.find(condition => condition.type === 'Ready').status === 'True' ? 'Running' : 'NotReady', + cpu: `${Math.round((node.status.allocatable.cpu / node.status.capacity.cpu) * 100)}%`, + memory: `${Math.round((node.status.allocatable.memory / node.status.capacity.memory) * 100)}%`, + })); + }; + + const getPodStatuses = async (endpoint) => { + const response = await axiosInstance.get(`${endpoint}/api/v1/pods`); + return response.data.items.map(pod => ({ + name: pod.metadata.name, + status: pod.status.phase, + image: pod.spec.containers[0].image, + })); + }; + + return ( +
+
+
+
+
+ +
+ + + +
+
+ + [node.name, node.status, node.cpu, node.memory])} + /> + +
+ + [pod.name, pod.status, pod.image])} + /> + +
+
+
+ ); +}; + +export default Dashboard; diff --git a/frontend/src/components/ImageProcessor.tsx b/frontend/src/components/ImageProcessor.tsx index bdcae19..315f6f4 100644 --- a/frontend/src/components/ImageProcessor.tsx +++ b/frontend/src/components/ImageProcessor.tsx @@ -1,18 +1,28 @@ import React, { useState } from 'react'; +import { SelectValue, SelectTrigger, SelectItem, SelectContent, Select } from "@/components/ui/select"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; const BACK_END_URL = 'http://localhost:5000' +// const BACK_END_URL = ''; -const ImageProcessor = (): JSX.Element => { +type Operation = 'edge_detection' | 'color_inversion' | 'grayscale' | 'blur' | 'sharpen' | 'brightness_increase' | 'contrast_increase' | 'sharpening'; + +export default function Component() { const [file, setFile] = useState(null); + const [filePreview, setFilePreview] = useState('https://placehold.jp/1000x1000.png'); const [downloadUrl, setDownloadUrl] = useState(''); + const [operation, setOperation] = useState('edge_detection'); const handleFileChange = (event: React.ChangeEvent): void => { if (event.target.files) { - setFile(event.target.files[0]); + const selectedFile = event.target.files[0]; + setFile(selectedFile); + setFilePreview(URL.createObjectURL(selectedFile)); } }; - const processImage = async (operation: 'edge_detection' | 'color_inversion'): Promise => { + const processImage = async (): Promise => { if (!file) { alert('Please select a file first!'); return; @@ -31,7 +41,7 @@ const ImageProcessor = (): JSX.Element => { const data = await response.json(); if (response.ok) { setDownloadUrl(`${data.processed_file}`); - console.log(data.processed_file) + console.log(data.processed_file); alert('File processed successfully!'); } else { alert(data.error || 'Failed to process the file'); @@ -50,26 +60,37 @@ const ImageProcessor = (): JSX.Element => { }; return ( -
-

Image Processing

-
- - +
+ +
+ + +
-
- - +
+ Processed Image
- -
); -}; - -export default ImageProcessor; +} diff --git a/frontend/src/components/ImageSideBar.tsx b/frontend/src/components/ImageSideBar.tsx new file mode 100644 index 0000000..c2ec33f --- /dev/null +++ b/frontend/src/components/ImageSideBar.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +type ImageSidebarProps = { + name: string; + previews: string[]; + files: File[]; +}; + +const ImageSidebar: React.FC = ({ name, previews, files }) => { + return ( +
+

{name}

+ {previews.map((preview, index) => ( +
+

{files[index].name}

+ {files[index].name} +
+ ))} +
+ ); +}; + +export default ImageSidebar; + diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx new file mode 100644 index 0000000..0ba4277 --- /dev/null +++ b/frontend/src/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx new file mode 100644 index 0000000..677d05f --- /dev/null +++ b/frontend/src/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/frontend/src/components/ui/select.tsx b/frontend/src/components/ui/select.tsx new file mode 100644 index 0000000..fe56d4d --- /dev/null +++ b/frontend/src/components/ui/select.tsx @@ -0,0 +1,158 @@ +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { Check, ChevronDown, ChevronUp } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Select = SelectPrimitive.Root + +const SelectGroup = SelectPrimitive.Group + +const SelectValue = SelectPrimitive.Value + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)) +SelectContent.displayName = SelectPrimitive.Content.displayName + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)) +SelectItem.displayName = SelectPrimitive.Item.displayName + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +} diff --git a/frontend/src/index.css b/frontend/src/index.css index b5c61c9..b0e6fff 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,3 +1,76 @@ @tailwind base; -@tailwind components; -@tailwind utilities; + @tailwind components; + @tailwind utilities; + + @layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } + } + + @layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } + } \ No newline at end of file diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts new file mode 100644 index 0000000..d084cca --- /dev/null +++ b/frontend/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} -- cgit v1.2.3