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/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 ++++++++++++++++++++++++ 6 files changed, 493 insertions(+), 23 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 (limited to 'frontend/src/components') 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, +} -- cgit v1.2.3