Skip to content

Commit 4bc69f6

Browse files
committed
feat(core): server actions
move to server actions and new auth helpers BREAKING CHANGE: implicit auth -> pkce
1 parent bd1b249 commit 4bc69f6

Some content is hidden

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

79 files changed

+2878
-182922
lines changed

β€Ž.eslintrc.cjs

+18-8
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,9 @@ module.exports = {
1717
},
1818
plugins: ['@typescript-eslint', 'prettier'],
1919
rules: {
20-
'prettier/prettier': 'error',
20+
'prettier/prettier': 1,
2121
},
22-
files: [
23-
'src/**/*.ts',
24-
'src/**/*.tsx',
25-
'emails/**/*.ts',
26-
'emails/**/*.tsx',
27-
],
22+
files: ['src/**/*.ts', 'src/**/*.tsx'],
2823
},
2924
{
3025
extends: [
@@ -38,8 +33,8 @@ module.exports = {
3833
},
3934
plugins: [
4035
'@typescript-eslint',
41-
'prettier',
4236
'plugin:playwright/playwright-test',
37+
'prettier',
4338
],
4439
rules: {
4540
'prettier/prettier': 'error',
@@ -51,11 +46,26 @@ module.exports = {
5146
files: '*.mjs',
5247
rules: ruleOverrides,
5348
},
49+
// make nextconfig.mjs node environment
50+
{
51+
extends: ['eslint:recommended', 'prettier', 'node'],
52+
files: 'next.config.mjs',
53+
rules: ruleOverrides,
54+
},
5455
{
5556
extends: ['prettier'],
5657
files: '*.js',
5758
rules: ruleOverrides,
5859
},
60+
{
61+
extends: ['prettier'],
62+
files: '*.cjs',
63+
rules: ruleOverrides,
64+
parserOptions: {
65+
ecmaVersion: 2020,
66+
sourceType: 'module',
67+
},
68+
},
5969
],
6070
root: true,
6171
};

β€Žnext-env.d.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/// <reference types="next" />
22
/// <reference types="next/image-types/global" />
3-
/// <reference types="next/navigation-types/navigation" />
43

54
// NOTE: This file should not be edited
65
// see https://nextjs.org/docs/basic-features/typescript for more information.

β€Žnext.config.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export default {
22
experimental: {
33
appDir: true,
4+
serverActions: true,
45
},
56
images: {
67
remotePatterns: [

β€Žpackage.json

+20-3
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,33 @@
2424
"@commitlint/config-conventional": "^17.4.4",
2525
"@headlessui/react": "^1.7.11",
2626
"@heroicons/react": "^2.0.16",
27-
"@supabase/auth-helpers-nextjs": "^0.5.4",
27+
"@radix-ui/react-context-menu": "^2.1.3",
28+
"@radix-ui/react-dialog": "^1.0.2",
29+
"@radix-ui/react-dropdown-menu": "^2.0.2",
30+
"@radix-ui/react-hover-card": "^1.0.5",
31+
"@radix-ui/react-label": "^2.0.0",
32+
"@radix-ui/react-navigation-menu": "^1.1.1",
33+
"@radix-ui/react-popover": "^1.0.5",
34+
"@radix-ui/react-select": "^1.2.0",
35+
"@radix-ui/react-slider": "^1.1.1",
36+
"@radix-ui/react-switch": "^1.0.2",
37+
"@supabase/auth-helpers-nextjs": "^0.7.3",
2838
"@supabase/auth-helpers-react": "^0.3.1",
29-
"@supabase/supabase-js": "^2.8.0",
39+
"@supabase/supabase-js": "^2.31.0",
40+
"@tailwindcss/typography": "^0.5.9",
3041
"@tanstack/react-query": "^4.24.10",
31-
"next": "^13.2.1",
42+
"class-variance-authority": "^0.7.0",
43+
"clsx": "^2.0.0",
44+
"lucide-react": "0.206.0",
45+
"next": "^13.4.12",
3246
"next-seo": "^5.15.0",
3347
"next-sitemap": "^3.1.52",
3448
"react": "^18.2.0",
3549
"react-dom": "^18.2.0",
3650
"react-hot-toast": "^2.4.0",
51+
"tailwind-merge": "^1.14.0",
3752
"tailwindcss": "^3.2.7",
53+
"tailwindcss-animate": "^1.0.6",
3854
"url-join": "^5.0.0"
3955
},
4056
"devDependencies": {
@@ -56,6 +72,7 @@
5672
"env-cmd": "^10.1.0",
5773
"eslint": "^8.34.0",
5874
"eslint-config-esnext": "^4.1.0",
75+
"eslint-config-node": "^4.1.0",
5976
"eslint-config-prettier": "^8.6.0",
6077
"eslint-plugin-import": "^2.27.5",
6178
"eslint-plugin-playwright": "^0.12.0",

β€Žsrc/app/ItemsList.tsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
'use client';
22
import { Table } from '@/types';
33
import Link from 'next/link';
4-
import { useItems } from './utils/react-query-hooks';
54

6-
export const ItemsList = ({
7-
initialItems,
8-
}: {
9-
initialItems: Table<'items'>[];
10-
}) => {
11-
const { data: items } = useItems(initialItems);
5+
export const ItemsList = ({ items }: { items: Table<'items'>[] }) => {
126
return (
137
<div className="space-y-8">
148
<div className="flex justify-between items-baseline">

β€Žsrc/app/actions.ts

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use server';
2+
3+
import { createSupabaseServerActionClient } from '@/supabase-clients/createSupabaseServerActionClient';
4+
import {
5+
deleteItem,
6+
getAllItems,
7+
getItem,
8+
insertItem,
9+
updateItem,
10+
} from '../utils/supabase-queries';
11+
import { revalidatePath } from 'next/cache';
12+
13+
export async function insertItemAction(payload: {
14+
name: string;
15+
description: string;
16+
}) {
17+
const supabaseClient = createSupabaseServerActionClient();
18+
const data = await insertItem(supabaseClient, payload);
19+
revalidatePath('/');
20+
return data.id;
21+
}
22+
23+
export async function getAllItemsAction() {
24+
const supabaseClient = createSupabaseServerActionClient();
25+
return await getAllItems(supabaseClient);
26+
}
27+
28+
export async function getItemAction(id: string) {
29+
const supabaseClient = createSupabaseServerActionClient();
30+
return await getItem(supabaseClient, id);
31+
}
32+
33+
export async function updateItemAction(payload: {
34+
id: string;
35+
name: string;
36+
description: string;
37+
}) {
38+
const supabaseClient = createSupabaseServerActionClient();
39+
const data = await updateItem(supabaseClient, payload);
40+
revalidatePath('/');
41+
return data;
42+
}
43+
44+
export const deleteItemAction = async (id: string) => {
45+
const supabaseClient = createSupabaseServerActionClient();
46+
await deleteItem(supabaseClient, id);
47+
revalidatePath('/');
48+
};

β€Žsrc/app/globals.css

+79-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@tailwind base;
22
@tailwind components;
3+
@tailwind utilities;
34

45
html,
56
body {
@@ -14,7 +15,6 @@ body {
1415
width: 100%;
1516
}
1617

17-
@tailwind utilities;
1818

1919
.swiper {
2020
width: 640px;
@@ -29,3 +29,81 @@ body {
2929
font-weight: bold;
3030

3131
}
32+
33+
@layer base {
34+
:root {
35+
--background: 0 0% 100%;
36+
--foreground: 222.2 47.4% 11.2%;
37+
38+
--muted: 210 40% 96.1%;
39+
--muted-foreground: 215.4 16.3% 46.9%;
40+
41+
--popover: 0 0% 100%;
42+
--popover-foreground: 222.2 47.4% 11.2%;
43+
44+
--border: 214.3 31.8% 91.4%;
45+
--input: 214.3 31.8% 91.4%;
46+
47+
--card: 0 0% 100%;
48+
--card-foreground: 222.2 47.4% 11.2%;
49+
50+
--primary: 222.2 47.4% 11.2%;
51+
--primary-foreground: 210 40% 98%;
52+
53+
--secondary: 210 40% 96.1%;
54+
--secondary-foreground: 222.2 47.4% 11.2%;
55+
56+
--accent: 210 40% 96.1%;
57+
--accent-foreground: 222.2 47.4% 11.2%;
58+
59+
--destructive: 0 100% 50%;
60+
--destructive-foreground: 210 40% 98%;
61+
62+
--ring: 215 20.2% 65.1%;
63+
64+
--radius: 0.5rem;
65+
}
66+
67+
.dark {
68+
--background: 224 71% 4%;
69+
--foreground: 213 31% 91%;
70+
71+
--muted: 223 47% 11%;
72+
--muted-foreground: 215.4 16.3% 56.9%;
73+
74+
--accent: 216 34% 17%;
75+
--accent-foreground: 210 40% 98%;
76+
77+
--popover: 224 71% 4%;
78+
--popover-foreground: 215 20.2% 65.1%;
79+
80+
--border: 216 34% 17%;
81+
--input: 216 34% 17%;
82+
83+
--card: 224 71% 4%;
84+
--card-foreground: 213 31% 91%;
85+
86+
--primary: 210 40% 98%;
87+
--primary-foreground: 222.2 47.4% 1.2%;
88+
89+
--secondary: 222.2 47.4% 11.2%;
90+
--secondary-foreground: 210 40% 98%;
91+
92+
--destructive: 0 63% 31%;
93+
--destructive-foreground: 210 40% 98%;
94+
95+
--ring: 216 34% 17%;
96+
97+
--radius: 0.5rem;
98+
}
99+
}
100+
101+
@layer base {
102+
* {
103+
@apply border-border;
104+
}
105+
body {
106+
@apply bg-background text-foreground;
107+
font-feature-settings: "rlig" 1, "calt" 1;
108+
}
109+
}

β€Žsrc/app/item/ItemIdLayoutContext.tsx

-27
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use client';
2+
import { Button } from '@/components/ui/Button';
3+
import {
4+
Dialog,
5+
DialogContent,
6+
DialogDescription,
7+
DialogFooter,
8+
DialogHeader,
9+
DialogTitle,
10+
DialogTrigger,
11+
} from '@/components/ui/Dialog';
12+
import { useMutation } from '@tanstack/react-query';
13+
import { useRouter } from 'next/navigation';
14+
import { useRef, useState } from 'react';
15+
import { toast } from 'react-hot-toast';
16+
import Trash from 'lucide-react/dist/esm/icons/trash';
17+
18+
type Props = {
19+
itemId: string;
20+
deleteItemAction: (itemId: string) => Promise<void>;
21+
};
22+
23+
export const ConfirmDeleteItemDialog = ({
24+
itemId,
25+
deleteItemAction,
26+
}: Props) => {
27+
const [open, setOpen] = useState(false);
28+
const toastRef = useRef<string | null>(null);
29+
const router = useRouter();
30+
const { mutate, isLoading } = useMutation(
31+
async (id: string) => {
32+
return deleteItemAction(id);
33+
},
34+
{
35+
onMutate: () => {
36+
const toastId = toast.loading('Deleting item');
37+
toastRef.current = toastId;
38+
},
39+
onSuccess: () => {
40+
toast.success('Item deleted', { id: toastRef.current });
41+
toastRef.current = null;
42+
router.refresh();
43+
router.push('/');
44+
},
45+
onError: () => {
46+
toast.error('Failed to delete item', { id: toastRef.current });
47+
toastRef.current = null;
48+
},
49+
onSettled: () => {
50+
setOpen(false);
51+
},
52+
}
53+
);
54+
return (
55+
<Dialog open={open} onOpenChange={setOpen}>
56+
<DialogTrigger asChild>
57+
<Button variant="destructive">
58+
<Trash className="mr-1" /> Delete Item
59+
</Button>
60+
</DialogTrigger>
61+
<DialogContent className="sm:max-w-[425px]">
62+
<DialogHeader>
63+
<DialogTitle>Delete Item</DialogTitle>
64+
<DialogDescription>
65+
Are you sure you want to delete this item?
66+
</DialogDescription>
67+
</DialogHeader>
68+
<DialogFooter>
69+
<Button
70+
type="button"
71+
variant="destructive"
72+
disabled={isLoading}
73+
onClick={() => {
74+
mutate(itemId);
75+
}}
76+
>
77+
{isLoading ? `Deleting item...` : `Yes, delete`}
78+
</Button>
79+
<Button
80+
disabled={isLoading}
81+
type="button"
82+
variant="outline"
83+
onClick={() => {
84+
setOpen(false);
85+
}}
86+
>
87+
Cancel
88+
</Button>
89+
</DialogFooter>
90+
</DialogContent>
91+
</Dialog>
92+
);
93+
};

0 commit comments

Comments
Β (0)