├── .eslintrc.json ├── .gitignore ├── README.md ├── components.json ├── next.config.js ├── package.json ├── postcss.config.js ├── prisma └── schema.prisma ├── public ├── next.svg └── vercel.svg ├── src ├── app │ ├── (auth) │ │ ├── auth-callback │ │ │ └── page.tsx │ │ ├── signin │ │ │ └── page.tsx │ │ └── sso-callback │ │ │ └── page.tsx │ ├── (dashborad) │ │ └── dashboard │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ ├── _action │ │ └── revalidatePage.ts │ ├── _trpc │ │ └── client.ts │ ├── api │ │ └── trpc │ │ │ └── [trpc] │ │ │ └── route.ts │ ├── favicon.ico │ ├── layout.tsx │ └── page.tsx ├── components │ ├── auth │ │ ├── auth-callback.tsx │ │ ├── oauth-signin.tsx │ │ └── sso-callback.tsx │ ├── buttons │ │ ├── add-new-button.tsx │ │ ├── inventory-switcher.tsx │ │ └── theme-toggle.tsx │ ├── form │ │ └── add-new-task-form.tsx │ ├── icons.tsx │ ├── layout │ │ └── header.tsx │ ├── providers │ │ ├── TrpcProvider.tsx │ │ └── theme-provider.tsx │ ├── shells │ │ ├── data-table-shell.tsx │ │ └── index.tsx │ ├── table │ │ ├── data-table-column-header.tsx │ │ ├── data-table-faceted-filter.tsx │ │ ├── data-table-loading.tsx │ │ ├── data-table-pagination.tsx │ │ ├── data-table-toolbar.tsx │ │ ├── data-table-view-option.tsx │ │ └── data-table.tsx │ └── ui │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── command.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── popover.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── skeleton.tsx │ │ ├── table.tsx │ │ ├── textarea.tsx │ │ ├── toaster.tsx │ │ └── tooltip.tsx ├── db │ └── index.ts ├── env.mjs ├── hooks │ └── use-debounce.ts ├── lib │ ├── errors.ts │ ├── task.ts │ └── utils.ts ├── middleware.tsx ├── styles │ └── globals.css ├── trpc │ ├── index.ts │ └── trpc.ts └── types │ └── index.ts ├── tailwind.config.js ├── tailwind.config.ts ├── tsconfig.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | .env 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | https://github.com/sujjeee/inventory/assets/101963203/acfd7355-1d87-4e14-a6a7-b703dd46af69 2 | 3 | > [!WARNING] 4 | > Make sure to customize it according to your use case. 5 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | experimental: { 4 | serverActions: true, 5 | }, 6 | } 7 | 8 | module.exports = nextConfig 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inventory", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@clerk/nextjs": "^4.25.1", 13 | "@prisma/client": "^5.3.1", 14 | "@radix-ui/react-avatar": "^1.0.4", 15 | "@radix-ui/react-checkbox": "^1.0.4", 16 | "@radix-ui/react-dialog": "^1.0.5", 17 | "@radix-ui/react-dropdown-menu": "^2.0.6", 18 | "@radix-ui/react-icons": "^1.3.0", 19 | "@radix-ui/react-label": "^2.0.2", 20 | "@radix-ui/react-popover": "^1.0.7", 21 | "@radix-ui/react-select": "^2.0.0", 22 | "@radix-ui/react-separator": "^1.0.3", 23 | "@radix-ui/react-slot": "^1.0.2", 24 | "@radix-ui/react-tooltip": "^1.0.7", 25 | "@t3-oss/env-nextjs": "^0.6.1", 26 | "@tanstack/react-query": "^4.35.7", 27 | "@tanstack/react-table": "^8.10.3", 28 | "@trpc/client": "^10.38.5", 29 | "@trpc/next": "^10.38.5", 30 | "@trpc/react-query": "^10.38.5", 31 | "@trpc/server": "^10.38.5", 32 | "class-variance-authority": "^0.7.0", 33 | "clsx": "^2.0.0", 34 | "cmdk": "^0.2.0", 35 | "lucide-react": "^0.284.0", 36 | "next": "13.5.4", 37 | "next-themes": "^0.2.1", 38 | "react": "^18", 39 | "react-dom": "^18", 40 | "sonner": "^1.0.3", 41 | "tailwind-merge": "^1.14.0", 42 | "tailwindcss-animate": "^1.0.7", 43 | "zod": "^3.22.4" 44 | }, 45 | "devDependencies": { 46 | "@types/node": "^20", 47 | "@types/react": "^18", 48 | "@types/react-dom": "^18", 49 | "autoprefixer": "^10", 50 | "eslint": "^8", 51 | "eslint-config-next": "13.5.4", 52 | "postcss": "^8", 53 | "tailwindcss": "^3", 54 | "typescript": "^5" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | } 4 | 5 | datasource db { 6 | provider = "postgresql" 7 | url = env("DATABASE_URL") 8 | } 9 | 10 | model User { 11 | id String @id @unique 12 | email String @unique 13 | fullName String? 14 | createdAt DateTime @default(now()) 15 | updatedAt DateTime @updatedAt 16 | inventories Inventory[] 17 | } 18 | 19 | model Inventory { 20 | id String @id @default(cuid()) 21 | name String 22 | userId String 23 | user User @relation(fields: [userId], references: [id]) 24 | tasks Task[] 25 | } 26 | 27 | model Task { 28 | id String @id @default(cuid()) 29 | task String 30 | title String 31 | status TaskStatus @default(Todo) 32 | createdAt DateTime @default(now()) 33 | updatedAt DateTime @updatedAt 34 | inventoryId String 35 | inventory Inventory @relation(fields: [inventoryId], references: [id]) 36 | } 37 | 38 | enum TaskStatus { 39 | Todo 40 | InProgress 41 | Done 42 | Canceled 43 | } 44 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/(auth)/auth-callback/page.tsx: -------------------------------------------------------------------------------- 1 | import { Shell } from "@/components/shells" 2 | import AuthCallback from "@/components/auth/auth-callback" 3 | 4 | export default function SSOCallbackPage() { 5 | return ( 6 | 7 | 8 | 9 | ) 10 | } -------------------------------------------------------------------------------- /src/app/(auth)/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import { type Metadata } from "next" 2 | import { redirect } from "next/navigation" 3 | import { currentUser } from "@clerk/nextjs" 4 | 5 | import { 6 | Card, 7 | CardContent, 8 | CardDescription, 9 | CardHeader, 10 | CardTitle, 11 | } from "@/components/ui/card" 12 | 13 | import { Shell } from "@/components/shells" 14 | import OAuthSignin from "@/components/auth/oauth-signin" 15 | 16 | export const metadata: Metadata = { 17 | title: "Sign In", 18 | description: "Sign in to your account", 19 | } 20 | 21 | export default async function SignInPage() { 22 | const user = await currentUser() 23 | if (user) redirect("/") 24 | 25 | return ( 26 | 27 | 28 | 29 | Sign in 30 | 31 | Choose your preferred sign in method 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ) 40 | } -------------------------------------------------------------------------------- /src/app/(auth)/sso-callback/page.tsx: -------------------------------------------------------------------------------- 1 | import { type HandleOAuthCallbackParams } from "@clerk/types" 2 | 3 | import SSOCallback from "@/components/auth/sso-callback" 4 | import { Shell } from "@/components/shells" 5 | 6 | export interface SSOCallbackPageProps { 7 | searchParams: HandleOAuthCallbackParams 8 | } 9 | 10 | export default function SSOCallbackPage({ 11 | searchParams, 12 | }: SSOCallbackPageProps) { 13 | return ( 14 | 15 | 16 | 17 | ) 18 | } -------------------------------------------------------------------------------- /src/app/(dashborad)/dashboard/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Shell } from "@/components/shells"; 2 | import { DataTableLoading } from "@/components/table/data-table-loading"; 3 | 4 | 5 | export default function IndexLoading() { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } -------------------------------------------------------------------------------- /src/app/(dashborad)/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | import { Header } from '@/components/layout/header' 2 | import { Shell } from '@/components/shells' 3 | import { DataTableShell } from '@/components/shells/data-table-shell' 4 | import { db } from '@/db' 5 | import { currentUser } from '@clerk/nextjs' 6 | import { Task } from '@prisma/client' 7 | import { redirect } from 'next/navigation' 8 | 9 | interface IndexPageProps { 10 | searchParams: { 11 | [key: string]: string | string[] | undefined 12 | } 13 | } 14 | 15 | export default async function Dashboard({ searchParams }: IndexPageProps) { 16 | 17 | const user = await currentUser() 18 | 19 | if (!user || !user.id) redirect('/auth-callback?origin=/') 20 | 21 | const dbUser = await db.user.findFirst({ 22 | where: { 23 | id: user.id 24 | } 25 | }) 26 | 27 | if (!dbUser) redirect('/auth-callback?origin=/') 28 | 29 | const { id, page, per_page, sort, title, status } = searchParams 30 | 31 | const idString = typeof id === 'string' ? id : undefined; 32 | 33 | const limit = typeof per_page === "string" ? parseInt(per_page) : 10 34 | 35 | const skip = typeof page === "string" 36 | ? parseInt(page) > 0 37 | ? (parseInt(page) - 1) * limit 38 | : 0 39 | : 0 40 | 41 | const [column, order] = typeof sort === "string" 42 | ? (sort.split(".") as [ 43 | keyof Task | undefined, 44 | "asc" | "desc" | undefined, 45 | ]) 46 | : [] 47 | 48 | const statuses = typeof status === "string" ? (status.split(".") as Task["status"][]) : [] 49 | 50 | const { allTasks, totalTasks } = await db.$transaction(async (tx) => { 51 | 52 | const userInventory = await tx.user.findFirst({ 53 | where: { 54 | id: user.id 55 | }, 56 | select: { 57 | inventories: { 58 | take: 1, 59 | }, 60 | } 61 | }) 62 | const inventoryID = idString ? idString : userInventory?.inventories[0]?.id 63 | 64 | console.log("inventoryID", inventoryID) 65 | const allTasks = inventoryID ? await tx.task.findMany({ 66 | take: limit, 67 | skip: skip, 68 | where: { 69 | inventoryId: inventoryID, 70 | title: typeof title === "string" ? { contains: title } : undefined, 71 | status: statuses.length > 0 ? { in: statuses } : undefined, 72 | }, 73 | orderBy: { 74 | [column ?? 'id']: order ?? 'desc', 75 | }, 76 | }) : [] 77 | 78 | const totalTasks = inventoryID ? await tx.task.count({ 79 | where: { 80 | inventoryId: inventoryID, 81 | title: typeof title === "string" ? { contains: title } : undefined, 82 | status: statuses.length > 0 ? { in: statuses } : undefined, 83 | }, 84 | }) : 0 85 | 86 | return { 87 | allTasks, 88 | totalTasks 89 | } 90 | }) 91 | 92 | const pageCount = Math.ceil(totalTasks / limit) 93 | 94 | return ( 95 | 96 |
97 | 98 | 99 | ) 100 | } 101 | -------------------------------------------------------------------------------- /src/app/_action/revalidatePage.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | 3 | import { revalidatePath } from 'next/cache' 4 | 5 | export async function revalidatePage() { 6 | console.log("hitted for relvatidate") 7 | revalidatePath('/dashboard') 8 | } 9 | -------------------------------------------------------------------------------- /src/app/_trpc/client.ts: -------------------------------------------------------------------------------- 1 | import { AppRouter } from "@/trpc"; 2 | import { createTRPCReact } from "@trpc/react-query"; 3 | 4 | 5 | export const trpc = createTRPCReact({}) -------------------------------------------------------------------------------- /src/app/api/trpc/[trpc]/route.ts: -------------------------------------------------------------------------------- 1 | import { appRouter } from '@/trpc'; 2 | import { fetchRequestHandler } from '@trpc/server/adapters/fetch'; 3 | 4 | const handler = (req: Request) => 5 | fetchRequestHandler({ 6 | endpoint: '/api/trpc', 7 | req, 8 | router: appRouter, 9 | createContext: () => ({}) 10 | }); 11 | 12 | export { handler as GET, handler as POST }; -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sujjeee/inventory/63b8b5ca1765edbb6387a625b7e9f8a3b0cae380/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import type { Metadata } from 'next' 3 | import { Inter } from 'next/font/google' 4 | import { ClerkProvider } from '@clerk/nextjs' 5 | import TrpcProvider from '@/components/providers/TrpcProvider' 6 | import { Toaster } from "@/components/ui/toaster" 7 | import { ThemeProvider } from '@/components/providers/theme-provider' 8 | 9 | const inter = Inter({ subsets: ['latin'] }) 10 | 11 | export const metadata: Metadata = { 12 | title: 'Inventory App', 13 | description: 'Manage you task with ease.', 14 | } 15 | 16 | 17 | export default async function RootLayout({ 18 | children, 19 | }: { 20 | children: React.ReactNode 21 | }) { 22 | return ( 23 | 24 | 25 | 26 | 27 | 33 | {children} 34 | 35 | 36 | 37 | 38 | 39 | 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { Shell } from '@/components/shells' 3 | import { currentUser } from '@clerk/nextjs' 4 | import Link from 'next/link' 5 | 6 | export default async function Home() { 7 | const user = await currentUser() 8 | return ( 9 | 10 | {user ? ( 11 | <> 12 | Hello {user.firstName} 👋 13 | 14 | Visit your dashboard. 15 | 16 | 17 | ) : ( 18 | 'Hello Guest' 19 | )} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/components/auth/auth-callback.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { trpc } from '@/app/_trpc/client' 4 | import { useRouter, useSearchParams } from 'next/navigation' 5 | 6 | export default function AuthCallback() { 7 | const router = useRouter() 8 | 9 | const searchParams = useSearchParams() 10 | const origin = searchParams.get('origin') 11 | 12 | trpc.authCallback.useQuery(undefined, { 13 | onSuccess: ({ success }) => { 14 | if (success) { 15 | // user is synced to db 16 | router.push(origin ? `/${origin}` : '/dashboard') 17 | } 18 | }, 19 | onError: (err) => { 20 | if (err.data?.code === 'UNAUTHORIZED') { 21 | router.push('/signin') 22 | } 23 | }, 24 | retry: true, 25 | retryDelay: 500, 26 | }) 27 | 28 | return ( 29 |
30 |

31 | Setting up your account... 32 |

33 |

You will be redirected automatically.

34 |
35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/components/auth/oauth-signin.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | 4 | import React from 'react' 5 | import { isClerkAPIResponseError, useSignIn } from "@clerk/nextjs" 6 | import { type OAuthStrategy } from "@clerk/types" 7 | import { toast } from "sonner" 8 | 9 | import { Button } from "@/components/ui/button" 10 | import { Icons } from "@/components/icons" 11 | 12 | const oauthProviders = [ 13 | { name: "Google", strategy: "oauth_google", icon: "google" }, 14 | { name: "Github", strategy: "oauth_github", icon: "gitHub" }, 15 | ] satisfies { 16 | name: string 17 | icon: keyof typeof Icons 18 | strategy: OAuthStrategy 19 | }[] 20 | 21 | interface OAuthSigninProps { } 22 | 23 | const OAuthSignin: React.FC = ({ }) => { 24 | const [isLoading, setIsLoading] = React.useState(null) 25 | const { signIn, isLoaded: signInLoaded } = useSignIn() 26 | 27 | async function oauthSignIn(provider: OAuthStrategy) { 28 | if (!signInLoaded) return null 29 | try { 30 | setIsLoading(provider) 31 | await signIn.authenticateWithRedirect({ 32 | strategy: provider, 33 | redirectUrl: "/sso-callback", 34 | redirectUrlComplete: "/", 35 | }) 36 | } catch (error) { 37 | setIsLoading(null) 38 | 39 | const unknownError = "Something went wrong, please try again." 40 | 41 | isClerkAPIResponseError(error) 42 | ? toast.error(error.errors[0]?.longMessage ?? unknownError) 43 | : toast.error(unknownError) 44 | } 45 | } 46 | return ( 47 |
48 | {oauthProviders.map((provider) => { 49 | const Icon = Icons[provider.icon] 50 | 51 | return ( 52 | 70 | ) 71 | })} 72 |
73 | ) 74 | } 75 | 76 | export default OAuthSignin -------------------------------------------------------------------------------- /src/components/auth/sso-callback.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { useClerk } from "@clerk/nextjs" 5 | 6 | import { Icons } from "@/components/icons" 7 | import { type SSOCallbackPageProps } from "@/app/(auth)/sso-callback/page" 8 | 9 | export default function SSOCallback({ searchParams }: SSOCallbackPageProps) { 10 | const { handleRedirectCallback } = useClerk() 11 | 12 | React.useEffect(() => { 13 | void handleRedirectCallback(searchParams) 14 | }, [searchParams, handleRedirectCallback]) 15 | 16 | return ( 17 |
23 |
25 | ) 26 | } -------------------------------------------------------------------------------- /src/components/buttons/add-new-button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { buttonVariants } from "@/components/ui/button" 3 | import { 4 | Dialog, 5 | DialogContent, 6 | DialogDescription, 7 | DialogHeader, 8 | DialogTitle, 9 | DialogTrigger, 10 | } from "@/components/ui/dialog" 11 | import { cn } from '@/lib/utils' 12 | import { PlusCircledIcon } from '@radix-ui/react-icons' 13 | import AddNewTaskForm from '../form/add-new-task-form' 14 | 15 | export default function AddNewTaskButton() { 16 | return ( 17 | 18 | 19 |
28 |
31 |
32 | 33 | 34 | Add new item 35 | 36 | Enter the details of your new item. 37 | 38 | 39 | 40 | 41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/components/buttons/inventory-switcher.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { 5 | CaretSortIcon, 6 | CheckIcon, 7 | PlusCircledIcon, 8 | } from "@radix-ui/react-icons" 9 | 10 | import { cn } from "@/lib/utils" 11 | import { 12 | Avatar, 13 | AvatarFallback, 14 | AvatarImage, 15 | } from "@/components/ui/avatar" 16 | import { Button } from "@/components/ui/button" 17 | import { 18 | Command, 19 | CommandEmpty, 20 | CommandGroup, 21 | CommandInput, 22 | CommandItem, 23 | CommandList, 24 | CommandSeparator, 25 | } from "@/components/ui/command" 26 | import { 27 | Dialog, 28 | DialogContent, 29 | DialogDescription, 30 | DialogFooter, 31 | DialogHeader, 32 | DialogTitle, 33 | DialogTrigger, 34 | } from "@/components/ui/dialog" 35 | import { Input } from "@/components/ui/input" 36 | import { Label } from "@/components/ui/label" 37 | import { 38 | Popover, 39 | PopoverContent, 40 | PopoverTrigger, 41 | } from "@/components/ui/popover" 42 | import { trpc } from "@/app/_trpc/client" 43 | import { Icons } from "@/components/icons" 44 | import { Skeleton } from "@/components/ui/skeleton" 45 | import { inferRouterOutputs } from "@trpc/server" 46 | import { AppRouter } from "@/trpc" 47 | import { useRouter } from "next/navigation" 48 | 49 | type RouterOutput = inferRouterOutputs 50 | type Inventory = RouterOutput['getInventory'] 51 | type PopoverTriggerProps = React.ComponentPropsWithoutRef 52 | interface InventorySwitcherProps extends PopoverTriggerProps { 53 | id: string | undefined 54 | } 55 | 56 | export default function InventorySwitcher({ className, id }: InventorySwitcherProps) { 57 | 58 | const router = useRouter() 59 | 60 | const [inventory, setInventory] = React.useState([]); 61 | const [open, setOpen] = React.useState(false) 62 | const [inventoryName, setInventoryName] = React.useState('') 63 | const [showNewTeamDialog, setShowNewTeamDialog] = React.useState(false) 64 | const [selectedTeam, setSelectedTeam] = React.useState( 65 | inventory[0] 66 | ) 67 | const { data: getInventory, isLoading: isFetching } = trpc.getInventory.useQuery(undefined, { 68 | onSuccess: (data) => { 69 | if (data.length > 0) { 70 | setInventory(data) 71 | const matchingItem = data.find((item) => item.id === id); 72 | if (matchingItem) { 73 | setSelectedTeam(matchingItem); 74 | router.push(`/dashboard?id=${matchingItem.id}`); 75 | return; // Exit the function if a match is found 76 | } 77 | setSelectedTeam(data[0]); 78 | router.push(`/dashboard?id=${data[0].id}`); 79 | } 80 | } 81 | }) 82 | 83 | const { mutate: createInventory, isLoading: iscreating } = trpc.createInventory.useMutation({ 84 | onSuccess: (data) => { 85 | console.log("data form create invenroty", data) 86 | if (data) { 87 | setInventory((prevInventory) => [...prevInventory, data]); 88 | setSelectedTeam(data); 89 | router.push(`/dashboard?id=${data.id}`); 90 | setShowNewTeamDialog(false) 91 | } 92 | } 93 | }); 94 | 95 | function getNameById(id: any) { 96 | const item = inventory.find(item => item.id === id); 97 | return item ? item.name : 'Not Found'; 98 | } 99 | return ( 100 | 101 | 102 | 103 | 120 | 121 | 122 | 123 | 124 | 125 | No team found. 126 | 127 | {isFetching 128 | ? () 129 | : (<> 130 | {getInventory?.map((group) => { 131 | // console.log("groups", group.id) 132 | // console.log("selectedTeam?.id", selectedTeam?.id) 133 | return ( 134 | { 137 | setSelectedTeam(group) 138 | setOpen(false) 139 | router.push(`/dashboard?id=${group.id}`) 140 | }} 141 | className="text-sm" 142 | > 143 | 144 | 149 | SC 150 | 151 | {group.name} 152 | 160 | ) 161 | })} 162 | )} 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | { 172 | setOpen(false) 173 | setShowNewTeamDialog(true) 174 | }} 175 | > 176 | 177 | Create Team 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | Create inventory 188 | 189 | Add a new inventory to manage tasks. 190 | 191 | 192 |
193 |
194 |
195 | 196 | { 200 | setInventoryName(e.target.value) 201 | }} /> 202 |
203 |
204 |
205 | 206 | 215 | 233 | 234 |
235 |
236 | ) 237 | } -------------------------------------------------------------------------------- /src/components/buttons/theme-toggle.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { MoonIcon, SunIcon } from "@radix-ui/react-icons" 4 | import { useTheme } from "next-themes" 5 | 6 | import { Button } from "@/components/ui/button" 7 | 8 | export function ThemeToggle() { 9 | const { setTheme, theme } = useTheme() 10 | return ( 11 | 26 | ) 27 | } -------------------------------------------------------------------------------- /src/components/form/add-new-task-form.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import React from 'react' 4 | import { Textarea } from '../ui/textarea' 5 | import { Label } from '../ui/label' 6 | import { Input } from '../ui/input' 7 | import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '../ui/select' 8 | import { TaskStatus } from '@prisma/client' 9 | import { Button } from '../ui/button' 10 | import { Icons } from '../icons' 11 | import { useSearchParams } from 'next/navigation' 12 | import { trpc } from '@/app/_trpc/client' 13 | import { toast } from 'sonner' 14 | import { revalidatePage } from '@/app/_action/revalidatePage' 15 | 16 | export default function AddNewTaskForm() { 17 | 18 | const searchParams = useSearchParams() 19 | const id = searchParams.get('id') 20 | 21 | const [formData, setFormData] = React.useState({ 22 | task: '', 23 | description: '', 24 | status: 'Todo', 25 | }); 26 | 27 | const handleInputChange = (e: any) => { 28 | const { name, value } = e.target; 29 | setFormData({ 30 | ...formData, 31 | [name]: value, 32 | }); 33 | }; 34 | 35 | const taskStatusKeys = Object.keys(TaskStatus).filter( 36 | (key) => isNaN(Number(key)) 37 | ) as [keyof typeof TaskStatus]; 38 | 39 | // const { mutate: createInventoryTask, isLoading: iscreating } = trpc.createInventoryTask.useMutation(); 40 | 41 | const { mutate: createInventoryTask, isLoading: iscreating } = trpc.createInventoryTask.useMutation({ 42 | onSuccess: async () => { 43 | toast.success("Created new task!") 44 | await revalidatePage() 45 | // Reset the form fields to their initial state 46 | setFormData({ 47 | task: '', 48 | description: '', 49 | status: 'Todo', 50 | }); 51 | }, 52 | }) 53 | 54 | return ( 55 |
56 |
57 | 58 | 65 |
66 |
67 | 68 |