Skip to content

Commit 37709e0

Browse files
authored
feat: Initialize project using v0.dev (#1)
1 parent 9708d70 commit 37709e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4907
-1291
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,6 @@ dist
127127
.yarn/unplugged
128128
.yarn/build-state.yml
129129
.yarn/install-state.gz
130-
.pnp.*
130+
.pnp.*
131+
132+
.turbo/

.husky/pre-commit

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env sh
2+
. "$(dirname -- "$0")/_/husky.sh"
3+
4+
npx lint-staged

apps/webapp/components.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "new-york",
4+
"rsc": true,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "tailwind.config.ts",
8+
"css": "src/app/globals.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib",
18+
"hooks": "@/hooks"
19+
},
20+
"iconLibrary": "lucide"
21+
}

apps/webapp/eslint.config.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ const compat = new FlatCompat({
1111

1212
const eslintConfig = [
1313
...compat.extends("next/core-web-vitals", "next/typescript"),
14+
15+
{
16+
"@typescript-eslint/no-unused-vars": "off",
17+
},
1418
];
1519

1620
export default eslintConfig;

apps/webapp/next-env.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

apps/webapp/next.config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
4-
/* config options here */
4+
experimental: {
5+
typedRoutes: true,
6+
},
57
};
68

79
export default nextConfig;

apps/webapp/package.json

+20-6
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,39 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"dev": "next dev",
6+
"dev": "next dev -p 9000",
77
"build": "next build",
88
"start": "next start",
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12+
"@auth/drizzle-adapter": "^1.7.4",
13+
"@trpc/client": "11.0.0-rc.666",
14+
"@trpc/next": "11.0.0-rc.666",
15+
"@trpc/react-query": "11.0.0-rc.666",
16+
"@trpc/server": "11.0.0-rc.666",
17+
"class-variance-authority": "^0.7.1",
18+
"clsx": "^2.1.1",
19+
"drizzle-orm": "^0.38.3",
20+
"lucide-react": "^0.469.0",
21+
"next": "15.1.1-canary.19",
22+
"next-auth": "^4.24.11",
23+
"pg": "^8.13.1",
1224
"react": "^19.0.0",
1325
"react-dom": "^19.0.0",
14-
"next": "15.1.1-canary.19"
26+
"tailwind-merge": "^2.6.0",
27+
"tailwindcss-animate": "^1.0.7",
28+
"zod": "^3.24.1"
1529
},
1630
"devDependencies": {
17-
"typescript": "^5",
31+
"@eslint/eslintrc": "^3",
1832
"@types/node": "^20",
1933
"@types/react": "^19",
2034
"@types/react-dom": "^19",
21-
"postcss": "^8",
22-
"tailwindcss": "^3.4.1",
2335
"eslint": "^9",
2436
"eslint-config-next": "15.1.1-canary.19",
25-
"@eslint/eslintrc": "^3"
37+
"postcss": "^8",
38+
"tailwindcss": "^3.4.1",
39+
"typescript": "^5"
2640
}
2741
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import NextAuth from "next-auth";
2+
import { authOptions } from "@/lib/server/auth-optionns";
3+
4+
const handler = NextAuth(authOptions);
5+
6+
export { handler as GET, handler as POST };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
2+
import { appRouter } from "@/server/router";
3+
import { createContext } from "@/server/trpc";
4+
5+
const handler = (req: Request) =>
6+
fetchRequestHandler({
7+
endpoint: "/api/trpc",
8+
req,
9+
router: appRouter,
10+
createContext,
11+
});
12+
13+
export { handler as GET, handler as POST };
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import { trpc } from "@/utils/trpc";
5+
import { withAuth } from "@/components/withAuth";
6+
7+
function Devices() {
8+
const [devices, setDevices] = useState([]);
9+
const { data: fetchedDevices, isLoading } = trpc.getDevices.useQuery();
10+
const registerDeviceMutation = trpc.registerDevice.useMutation();
11+
12+
useEffect(() => {
13+
if (fetchedDevices) {
14+
setDevices(fetchedDevices);
15+
}
16+
}, [fetchedDevices]);
17+
18+
const registerDevice = async () => {
19+
try {
20+
const newDevice = await registerDeviceMutation.mutateAsync({
21+
name: "New Device",
22+
});
23+
setDevices([...devices, newDevice]);
24+
} catch (error) {
25+
console.error("Error registering device:", error);
26+
}
27+
};
28+
29+
if (isLoading) return <div>Loading...</div>;
30+
31+
return (
32+
<div className="space-y-6">
33+
<h1 className="text-3xl font-bold">Manage Devices</h1>
34+
<button
35+
onClick={registerDevice}
36+
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
37+
>
38+
Register New Device
39+
</button>
40+
<div className="bg-white rounded-lg shadow-md p-6">
41+
<h2 className="text-xl font-semibold mb-4">Registered Devices</h2>
42+
{devices.length === 0 ? (
43+
<p>No devices registered yet.</p>
44+
) : (
45+
<ul className="space-y-2">
46+
{devices.map((device, index) => (
47+
<li key={index} className="bg-gray-50 p-3 rounded-md">
48+
{device.name}
49+
</li>
50+
))}
51+
</ul>
52+
)}
53+
</div>
54+
</div>
55+
);
56+
}
57+
58+
export default withAuth(Devices);

apps/webapp/src/app/app/layout.tsx

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { authOptions } from "@/lib/server/auth-optionns";
2+
import { getServerSession } from "next-auth/next";
3+
import { redirect } from "next/navigation";
4+
5+
export default async function AppLayout({
6+
children,
7+
}: {
8+
children: React.ReactNode;
9+
}) {
10+
const session = await getServerSession(authOptions);
11+
12+
if (!session) {
13+
redirect("/");
14+
}
15+
16+
return (
17+
<div className="min-h-screen bg-gray-100">
18+
<nav className="bg-white shadow-sm">
19+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
20+
<div className="flex justify-between h-16">
21+
<div className="flex">
22+
<div className="flex-shrink-0 flex items-center">
23+
<span className="text-xl font-bold">Dudy Trustable</span>
24+
</div>
25+
</div>
26+
<div className="flex items-center">
27+
<span className="text-sm text-gray-500">
28+
Signed in as {session.user?.email}
29+
</span>
30+
</div>
31+
</div>
32+
</div>
33+
</nav>
34+
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">{children}</main>
35+
</div>
36+
);
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"use client";
2+
3+
import { use, useState } from "react";
4+
import { trpc } from "@/utils/trpc";
5+
import { withAuth } from "@/components/withAuth";
6+
7+
type Props = {
8+
params: Promise<{
9+
packageId: string;
10+
}>;
11+
};
12+
13+
function PackageApprovalConfig(props: Props) {
14+
const params = use(props.params);
15+
const [newGroupName, setNewGroupName] = useState("");
16+
const { data: packageDetails } = trpc.getPackageDetails.useQuery({
17+
packageId: parseInt(params.packageId),
18+
});
19+
const {
20+
data: approvalGroups,
21+
isLoading,
22+
refetch,
23+
} = trpc.getPackageApprovalGroups.useQuery({
24+
packageId: parseInt(params.packageId),
25+
});
26+
const createGroupMutation = trpc.createApprovalGroup.useMutation();
27+
28+
const handleCreateGroup = async (e: React.FormEvent) => {
29+
e.preventDefault();
30+
if (newGroupName.trim()) {
31+
try {
32+
await createGroupMutation.mutateAsync({
33+
packageId: parseInt(params.packageId),
34+
name: newGroupName.trim(),
35+
});
36+
setNewGroupName("");
37+
refetch();
38+
} catch (error) {
39+
console.error("Error creating group:", error);
40+
alert("Failed to create group. Please try again.");
41+
}
42+
}
43+
};
44+
45+
if (isLoading) return <div>Loading...</div>;
46+
47+
if (!packageDetails?.isOwner) {
48+
return (
49+
<div className="space-y-6">
50+
<h1 className="text-3xl font-bold">Package Approval Configuration</h1>
51+
<p className="text-red-500">
52+
You do not have permission to modify this package&apos;s approval
53+
configuration.
54+
</p>
55+
</div>
56+
);
57+
}
58+
59+
return (
60+
<div className="space-y-6">
61+
<h1 className="text-3xl font-bold">Package Approval Configuration</h1>
62+
<div className="bg-white rounded-lg shadow-md p-6">
63+
<h2 className="text-xl font-semibold mb-4">Approval Groups</h2>
64+
<ul className="space-y-2 mb-4">
65+
{approvalGroups?.map((group) => (
66+
<li key={group.id} className="bg-gray-50 p-3 rounded-md">
67+
{group.name}
68+
</li>
69+
))}
70+
</ul>
71+
<form onSubmit={handleCreateGroup} className="flex space-x-2">
72+
<input
73+
type="text"
74+
value={newGroupName}
75+
onChange={(e) => setNewGroupName(e.target.value)}
76+
placeholder="New group name"
77+
className="flex-grow px-3 py-2 border border-gray-300 rounded-md"
78+
/>
79+
<button
80+
type="submit"
81+
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
82+
>
83+
Create Group
84+
</button>
85+
</form>
86+
</div>
87+
</div>
88+
);
89+
}
90+
91+
export default withAuth(PackageApprovalConfig);

0 commit comments

Comments
 (0)