diff --git a/frontend/package-lock.json b/frontend/package-lock.json index befafd8..62dc828 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,6 +13,7 @@ "axios": "^1.13.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "lucide-react": "^0.577.0", "radix-ui": "^1.4.3", "react": "^19.2.0", @@ -4803,6 +4804,22 @@ "node": ">=6" } }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/code-block-writer": { "version": "13.0.3", "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 65d0320..5230bf4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "axios": "^1.13.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "lucide-react": "^0.577.0", "radix-ui": "^1.4.3", "react": "^19.2.0", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2146b76..ce13fff 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -11,6 +11,7 @@ import ProtectedRoute from "./auth/ProtectedRoute"; import PublicRoute from "./auth/PublicRoute"; import {AuthProvider} from "./context/AuthContext"; import Department from "./pages/Department"; +import Doctor from "./pages/Doctor"; export default function App() { return ( @@ -25,6 +26,7 @@ export default function App() { }> } /> } /> + } /> diff --git a/frontend/src/api/department.ts b/frontend/src/api/department.ts index 8c16506..86fc40e 100644 --- a/frontend/src/api/department.ts +++ b/frontend/src/api/department.ts @@ -10,15 +10,11 @@ export interface Department { services: string; } -/* ---------------- GET ALL ---------------- */ - export const getDepartmentsApi = async () => { const res = await apiClient.get("/departments/getAll"); return res.data; }; -/* ---------------- CREATE ---------------- */ - export const createDepartmentApi = async (data: { departmentId: string; name: string; @@ -32,10 +28,8 @@ export const createDepartmentApi = async (data: { return res.data; }; -/* ---------------- UPDATE ---------------- */ - export const updateDepartmentApi = async ( - id: number, + departmentId: string, data: { name?: string; para1?: string; @@ -45,13 +39,11 @@ export const updateDepartmentApi = async ( services?: string; }, ) => { - const res = await apiClient.put(`/departments/${id}`, data); + const res = await apiClient.put(`/departments/${departmentId}`, data); return res.data; }; -/* ---------------- DELETE ---------------- */ - -export const deleteDepartmentApi = async (id: number) => { - const res = await apiClient.delete(`/departments/${id}`); +export const deleteDepartmentApi = async (departmentId: string) => { + const res = await apiClient.delete(`/departments/${departmentId}`); return res.data; }; diff --git a/frontend/src/api/doctor.ts b/frontend/src/api/doctor.ts new file mode 100644 index 0000000..f9ab808 --- /dev/null +++ b/frontend/src/api/doctor.ts @@ -0,0 +1,53 @@ +import apiClient from "@/api/client"; + +export interface Doctor { + doctorId: string; + name: string; + designation?: string; + workingStatus?: string; + qualification?: string; + departments: string[]; + timing: { + monday?: string; + tuesday?: string; + wednesday?: string; + thursday?: string; + friday?: string; + saturday?: string; + sunday?: string; + additional?: string; + }; +} + +export const getDoctorsApi = async () => { + const res = await apiClient.get("/doctors/getAll"); + return res.data; +}; + +export const getDoctorByIdApi = async (doctorId: string) => { + const res = await apiClient.get(`/doctors/${doctorId}`); + return res.data; +}; + +export const createDoctorApi = async (data: Doctor) => { + const res = await apiClient.post("/doctors", data); + return res.data; +}; + +export const updateDoctorApi = async ( + doctorId: string, + data: Partial, +) => { + const res = await apiClient.patch(`/doctors/${doctorId}`, data); + return res.data; +}; + +export const deleteDoctorApi = async (doctorId: string) => { + const res = await apiClient.delete(`/doctors/${doctorId}`); + return res.data; +}; + +export const getDoctorTimingApi = async (doctorId: string) => { + const res = await apiClient.get(`/doctors/getTimings/${doctorId}`); + return res.data; +}; diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index bacfa13..573bc08 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -12,8 +12,8 @@ export default function Sidebar() { path: "/department", }, { - name: "Blog", - path: "/blog", + name: "Doctor", + path: "/doctor", }, { name: "Subjects", diff --git a/frontend/src/components/ui/command.tsx b/frontend/src/components/ui/command.tsx new file mode 100644 index 0000000..b70649b --- /dev/null +++ b/frontend/src/components/ui/command.tsx @@ -0,0 +1,193 @@ +import * as React from "react" +import { Command as CommandPrimitive } from "cmdk" + +import { cn } from "@/lib/utils" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { + InputGroup, + InputGroupAddon, +} from "@/components/ui/input-group" +import { SearchIcon, CheckIcon } from "lucide-react" + +function Command({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandDialog({ + title = "Command Palette", + description = "Search for a command to run...", + children, + className, + showCloseButton = false, + ...props +}: React.ComponentProps & { + title?: string + description?: string + className?: string + showCloseButton?: boolean +}) { + return ( + + + {title} + {description} + + + {children} + + + ) +} + +function CommandInput({ + className, + ...props +}: React.ComponentProps) { + return ( +
+ + + + + + +
+ ) +} + +function CommandList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandEmpty({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + {children} + + + ) +} + +function CommandShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/frontend/src/components/ui/input-group.tsx b/frontend/src/components/ui/input-group.tsx new file mode 100644 index 0000000..256ba4b --- /dev/null +++ b/frontend/src/components/ui/input-group.tsx @@ -0,0 +1,156 @@ +"use client" + +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" + +function InputGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", + className + )} + {...props} + /> + ) +} + +const inputGroupAddonVariants = cva( + "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4", + { + variants: { + align: { + "inline-start": + "order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]", + "inline-end": + "order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]", + "block-start": + "order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2", + "block-end": + "order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2", + }, + }, + defaultVariants: { + align: "inline-start", + }, + } +) + +function InputGroupAddon({ + className, + align = "inline-start", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
{ + if ((e.target as HTMLElement).closest("button")) { + return + } + e.currentTarget.parentElement?.querySelector("input")?.focus() + }} + {...props} + /> + ) +} + +const inputGroupButtonVariants = cva( + "flex items-center gap-2 text-sm shadow-none", + { + variants: { + size: { + xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5", + sm: "", + "icon-xs": + "size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0", + "icon-sm": "size-8 p-0 has-[>svg]:p-0", + }, + }, + defaultVariants: { + size: "xs", + }, + } +) + +function InputGroupButton({ + className, + type = "button", + variant = "ghost", + size = "xs", + ...props +}: Omit, "size"> & + VariantProps) { + return ( +