diff --git a/.env.sample b/.env.sample index 8826df7..cc9f943 100644 --- a/.env.sample +++ b/.env.sample @@ -1,2 +1,2 @@ -IMMICH_URL="" +NEXT_PUBLIC_IMMICH_URL="" IMMICH_API_KEY="" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b35c7ea --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Immich Power Tools + +The code is pretty straight forward and easy to understand. Please just make sure you follow the below guidelines so that its easy for me to review and merge your PR. + + +### Structure + +- `components` - All the UI components +- `config` - All the configuration files and constants + - `config/routes.ts` - All the routes for the application +- `helpers` - All the helper functions for both data and modules +- `lib` - All the utility functions +- `pages` - All the pages based on [Next.js](https://nextjs.org/)'s page based routing +- `styles` - All the global styles (We don't touch this much as we are using Tailwind CSS) +- `types` - All the typescript types and interfaces (Make sure you create `.d.ts` files for each module) + +### API Infrastructure + +Since Immich API needs to send auth token in the headers, I've created a proxy api that matches the Immich's APIs path to forward the request to Immich's API with it's token (Which is stored in the `.env` file). This is done to avoid CORS issues and Security issues. + +You can find the API route here `pages/api/immich-proxy/[...path].ts` + +![Immich Proxy API](./screenshots/screenshot-infra.jpg) + + +### Thubmnails + +Since the `thumbnailPath` of Immich API doesnt give us a direct link to the image, I've created a proxy api that fetches the image and sends it back as a response. + +**For Person's Thumbnail** +You can find the API route here `pages/api/immich-proxy/thumbnail/[id].ts` \ No newline at end of file diff --git a/README.md b/README.md index 4e04a68..8563daf 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,54 @@ -# Immich Power Tools +# ![Logo](./public/favicon-32x32.png) Immich Power Tools -A unofficial immich client to provide better tools to organize and manage your immich account. +A unofficial immich client to provide better tools to organize and manage your immich account. Building it to speed up your workflows in Immich to organize your people and albums. -## Getting Started +
-First, run the development server: +### Back story + +Recently I've migrated my entire Google photos library to Immich, I was able to successfully migrate all my assets along with its albums to immich. But there were few things like people match that was lacking. I loved Immich UI on the whole but for organizing content I felt its quite restricted and I had to do a lot of things in bulk instead of opening each asset and doing it. Hence I built this tool (continuing to itereate) to make my life and any other Immich user's life easier. + +### Getting Started + +> [!NOTE] +> Please use `bun` to run the project. If you don't have `bun` installed, checkout [bun.sh](https://bun.sh/) for installation. + +Copy paste the `.env.example` to `.env` and fill in the values. ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +NEXT_PUBLIC_IMMICH_URL="" # Immich API URL +IMMICH_API_KEY="" # Immich API Key ``` + +Refer here for obtaining Immich API Key: https://immich.app/docs/features/command-line-interface#obtain-the-api-key + +Run the development server: + +```bash +bun run dev +``` + +### Features + +**Immich Related** + +- [x] Manage People +- [ ] Manage Albums +- [ ] Statistics + +**Tech Related** + +- [ ] Dark Mode +- [ ] Dockerize +- [ ] Authentication + +### Tech Stack + +- [Next.js](https://nextjs.org/) for infrastructure +- [Tailwind CSS](https://tailwindcss.com/) for styling +- [Shadcn](https://shadcn.com/) for UI Components +- [Axios](https://axios-http.com/) for API calls + +### Contributing + +Feel free to contribute to this project, I'm open to any suggestions and improvements. Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) for more details. \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 1bae1f3..07fe3ec 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 48e8c91..88d83e0 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,14 @@ "dependencies": { "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.2", "@tanstack/react-table": "^8.20.1", "@types/qs": "^6.9.15", "axios": "^1.7.4", diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 0000000..eb63c8c Binary files /dev/null and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000..e7d3408 Binary files /dev/null and b/public/android-chrome-512x512.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..4e5f8fb Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000..8f6408f Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000..e9d7f2a Binary files /dev/null and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico index 718d6fe..72421ec 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000..fab9b4a Binary files /dev/null and b/public/favicon.png differ diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/screenshots/screenshot-1.jpeg b/screenshots/screenshot-1.jpeg new file mode 100644 index 0000000..7295cca Binary files /dev/null and b/screenshots/screenshot-1.jpeg differ diff --git a/screenshots/screenshot-infra.jpg b/screenshots/screenshot-infra.jpg new file mode 100644 index 0000000..0cbcd07 Binary files /dev/null and b/screenshots/screenshot-infra.jpg differ diff --git a/src/components/layouts/PageLayout.tsx b/src/components/layouts/PageLayout.tsx new file mode 100644 index 0000000..5bfe866 --- /dev/null +++ b/src/components/layouts/PageLayout.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import Header from "../shared/Header"; +import { cn } from "@/lib/utils"; + +interface IProps { + children: React.ReactNode[] | React.ReactNode; +} +export default function PageLayout({ children }: IProps) { + const header = Array.isArray(children) + ? children.find((child) => React.isValidElement(child) && child.type === Header) + : null; + + const childrenWithoutHeader = Array.isArray(children) + ? children.filter((child) => { + return React.isValidElement(child) && child.type !== Header; + }) + : children; + + return ( + <> + {header} +
+ {childrenWithoutHeader} +
+ + ); +} diff --git a/src/components/layouts/RootLayout.tsx b/src/components/layouts/RootLayout.tsx index 90ac2eb..2270616 100644 --- a/src/components/layouts/RootLayout.tsx +++ b/src/components/layouts/RootLayout.tsx @@ -1,5 +1,6 @@ import { cn } from "@/lib/utils" +import Sidebar from "../shared/Sidebar" type RootLayoutProps = { @@ -8,10 +9,11 @@ type RootLayoutProps = { export default function RootLayout({ children }: RootLayoutProps) { return ( -
- {children} +
+ +
+ {children} +
) } diff --git a/src/components/people/PeopleList.tsx b/src/components/people/PeopleList.tsx index 975af48..3d4d89c 100644 --- a/src/components/people/PeopleList.tsx +++ b/src/components/people/PeopleList.tsx @@ -52,6 +52,7 @@ export default function PeopleList() { const router = useRouter() const { page = 1 } = router.query as { page: string } const [people, setPeople] = useState([]); + const [count, setCount] = useState(0); const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(null); @@ -62,6 +63,7 @@ export default function PeopleList() { }) .then((response) => { setPeople(response.people); + setCount(response.total); }) .catch((error) => { setErrorMessage(error.message); @@ -76,25 +78,15 @@ export default function PeopleList() { fetchData(); }, [page]); - const renderContent = () => { + if (loading) return ; if (errorMessage) return
{errorMessage}
; return ( -
+
{people.map((person) => ( ))}
) } - - return ( -
- {renderContent()} -
- -
-
- ); -} diff --git a/src/components/people/PersonItem.tsx b/src/components/people/PersonItem.tsx index e5efb52..ded39d7 100644 --- a/src/components/people/PersonItem.tsx +++ b/src/components/people/PersonItem.tsx @@ -8,6 +8,7 @@ import PersonHideCell from "./PersonHideCell"; import clsx from "clsx"; import Link from "next/link"; import { ENV } from "@/config/environment"; +import { ArrowUpRight } from "lucide-react"; interface IProps { person: IPerson; @@ -31,37 +32,62 @@ export default function PersonItem({ person }: IProps) { }); }; + const handleHide = (hidden: boolean) => { + setLoading(true); + return updatePerson(person.id, { + isHidden: hidden, + }) + .then(() => { + setFormData((person) => ({ + ...person, + isHidden: hidden, + })); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); + }; + return (
-
- -
- - View - -
-
- { - setFormData(newData); - }} +
+
{ + handleHide(!formData.isHidden); + }} + > +
+
+ + + +
+
+ +
{!editMode ? (

{ setEditMode((prev) => !prev); }} @@ -75,8 +101,9 @@ export default function PersonItem({ person }: IProps) { ) : ( { setFormData({ ...formData, name: e.target.value }); @@ -90,8 +117,9 @@ export default function PersonItem({ person }: IProps) { onBlur={handleEdit} /> )} - - +
+ +

); } diff --git a/src/components/people/PersonMergeDropdown.tsx b/src/components/people/PersonMergeDropdown.tsx index c95d2d9..00324ea 100644 --- a/src/components/people/PersonMergeDropdown.tsx +++ b/src/components/people/PersonMergeDropdown.tsx @@ -95,12 +95,11 @@ export function PersonMergeDropdown(
- - + { + if (typeof leftComponent === "string") { + return

{leftComponent}

; + } + return leftComponent; + }; + + const renderRightComponent = () => { + if (typeof rightComponent === "string") { + return

{rightComponent}

; + } + return rightComponent; + } + + return ( + <> + {typeof leftComponent === "string" && ( + + {leftComponent} - Immich Power Tools + + )} +
+ + + + + + +
+ + + Upgrade to Pro + + Unlock all features and get unlimited access to our support + team. + + + + + + +
+
+
+
+ {renderLeftComponent()} +
+ {renderRightComponent()} +
+ + ); +} diff --git a/src/components/shared/Sidebar.tsx b/src/components/shared/Sidebar.tsx new file mode 100644 index 0000000..467944b --- /dev/null +++ b/src/components/shared/Sidebar.tsx @@ -0,0 +1,70 @@ +import Link from "next/link"; +import { Bell, Camera, Package2 } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +import { sidebarNavs } from "@/config/constants/sidebarNavs"; +import { cn } from "@/lib/utils"; +import { useRouter } from "next/router"; +import { Badge } from "../ui/badge"; +import { ENV } from "@/config/environment"; + +export default function Sidebar() { + const router = useRouter(); + const { pathname } = router; + + return ( +
+
+
+ + Immich Power Tools + Immich Power Tools + +
+
+ +
+
+
+

Connected to

+ + {ENV.IMMICH_URL} + +
+
+ +

+ Made with ♥ by @zathvarun +

+
+
+ +
+
+ ); +} diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..e87d62b --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..77e9fb7 --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..0e4dccf --- /dev/null +++ b/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,203 @@ +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { + CheckIcon, + ChevronRightIcon, + DotFilledIcon, +} from "@radix-ui/react-icons" + +import { cn } from "@/lib/utils" + +const DropdownMenu = DropdownMenuPrimitive.Root + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger + +const DropdownMenuGroup = DropdownMenuPrimitive.Group + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal + +const DropdownMenuSub = DropdownMenuPrimitive.Sub + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +} diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..a92b8e0 --- /dev/null +++ b/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/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx new file mode 100644 index 0000000..639ffcb --- /dev/null +++ b/src/components/ui/sheet.tsx @@ -0,0 +1,138 @@ +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { Cross2Icon } from "@radix-ui/react-icons" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + + + Close + + {children} + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx new file mode 100644 index 0000000..53407fc --- /dev/null +++ b/src/components/ui/tooltip.tsx @@ -0,0 +1,41 @@ +import * as React from "react" +import * as TooltipPrimitive from "@radix-ui/react-tooltip" + +import { cn } from "@/lib/utils" + +const TooltipProvider = TooltipPrimitive.Provider + +const TooltipRoot = TooltipPrimitive.Root + +const TooltipTrigger = TooltipPrimitive.Trigger + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)) +TooltipContent.displayName = TooltipPrimitive.Content.displayName + + +const Tooltip: React.FC> = ({ + children, + ...props +}) => ( + + + {children} + + + +) + +export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider } diff --git a/src/config/constants/sidebarNavs.tsx b/src/config/constants/sidebarNavs.tsx new file mode 100644 index 0000000..2a608b5 --- /dev/null +++ b/src/config/constants/sidebarNavs.tsx @@ -0,0 +1,22 @@ +import { Home, User } from "lucide-react"; + + +export const sidebarNavs = [ + { + title: "Manage People", + link: "/", + icon: , + }, + { + title: "Statistics", + link: "/statistics", + icon: , + badge: "Comming Soon" + }, + { + title: "Albums", + link: "/albums", + icon: , + badge: "Comming Soon" + }, +]; \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 9eb810c..3070635 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,10 +1,21 @@ import { Inter } from "next/font/google"; import PeopleList from "@/components/people/PeopleList"; +import PageLayout from "@/components/layouts/PageLayout"; +import Header from "@/components/shared/Header"; +import { PeoplePagination } from "@/components/people/PeoplePagination"; const inter = Inter({ subsets: ["latin"] }); export default function Home() { return ( - + +
+ } + /> + + ); } diff --git a/src/types/person.d.ts b/src/types/person.d.ts index 9be8a8d..93641f1 100644 --- a/src/types/person.d.ts +++ b/src/types/person.d.ts @@ -10,4 +10,5 @@ export interface IPerson { interface IPeopleListResponse extends IListData{ people: IPerson[] + total: number } \ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts index 1fb21a9..d4d8bee 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -12,7 +12,7 @@ const config = { prefix: "", theme: { fontFamily: { - + mono: [ ...fontFamily.mono ], }, container: { center: true,