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`
+
+
+
+
+### 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
+#  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 (
-