├── .babelrc
├── .env
├── .eslintrc.json
├── .github
└── workflows
│ ├── build.yml
│ └── update.yml
├── .gitignore
├── .prettierrc
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── components
├── AuthGuard.tsx
├── Avatar.tsx
├── BreadCrumb.tsx
├── ManageMembers.tsx
├── NavBar.tsx
├── SpaceMembers.tsx
├── Spaces.tsx
├── TimeInfo.tsx
├── Todo.tsx
├── TodoList.tsx
└── WithNavBar.tsx
├── lib
├── context.ts
└── hooks
│ ├── __model_meta.ts
│ ├── account.ts
│ ├── index.ts
│ ├── list.ts
│ ├── space-user.ts
│ ├── space.ts
│ ├── todo.ts
│ └── user.ts
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── _app.tsx
├── api
│ ├── .DS_Store
│ ├── auth
│ │ └── [...nextauth].ts
│ └── model
│ │ └── [...path].ts
├── create-space.tsx
├── index.tsx
├── signin.tsx
├── signup.tsx
└── space
│ └── [slug]
│ ├── [listId]
│ └── index.tsx
│ └── index.tsx
├── postcss.config.js
├── prisma
├── migrations
│ ├── 20221014084317_init
│ │ └── migration.sql
│ ├── 20221020094651_upate_cli
│ │ └── migration.sql
│ ├── 20221103144245_drop_account_session
│ │ └── migration.sql
│ ├── 20221126150023_add_account
│ │ └── migration.sql
│ ├── 20221126151212_email_password_optional
│ │ └── migration.sql
│ ├── 20221126151510_refresh_token_expires
│ │ └── migration.sql
│ ├── 20221127033222_email_required
│ │ └── migration.sql
│ ├── 20230306121228_update
│ │ └── migration.sql
│ ├── 20230905035233_drop_aux_fields
│ │ └── migration.sql
│ ├── 20241222114017_add_space_owner
│ │ └── migration.sql
│ └── migration_lock.toml
└── schema.prisma
├── public
├── auth-bg.jpg
├── avatar.jpg
└── logo.png
├── schema.zmodel
├── server
├── auth.ts
├── db.ts
└── enhanced-db.ts
├── styles
└── globals.css
├── tailwind.config.js
├── tsconfig.json
└── types
├── next-auth.d.ts
└── next.d.ts
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["next/babel"],
3 | // "superjson-next" plugin uses superjson for serialization between getServerSideProps and client,
4 | // so that types like Date and BigInt are properly handled
5 | "plugins": ["superjson-next"]
6 | }
7 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | NEXTAUTH_SECRET=abc123
2 | DATABASE_URL="postgresql://postgres:abc123@localhost:5432/todo?schema=public"
3 | GITHUB_ID=
4 | GITHUB_SECRET=
5 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "next/core-web-vitals",
4 | "plugin:@typescript-eslint/recommended",
5 | "plugin:@typescript-eslint/recommended-type-checked"
6 | ],
7 | "parser": "@typescript-eslint/parser",
8 | "plugins": ["@typescript-eslint"],
9 | "parserOptions": {
10 | "ecmaVersion": 2020,
11 | "project": ["tsconfig.json"]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3 |
4 | name: CI
5 |
6 | env:
7 | DO_NOT_TRACK: '1'
8 |
9 | on:
10 | push:
11 | branches: ['main']
12 | pull_request:
13 | branches: ['main']
14 |
15 | jobs:
16 | build:
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v3
21 | - name: Use Node.js 20.x
22 | uses: actions/setup-node@v3
23 | with:
24 | node-version: 20.x
25 | cache: 'npm'
26 | - run: npm ci
27 | - run: npm run build
28 |
--------------------------------------------------------------------------------
/.github/workflows/update.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3 |
4 | name: Update ZenStack
5 |
6 | env:
7 | DO_NOT_TRACK: '1'
8 |
9 | on:
10 | workflow_dispatch:
11 | repository_dispatch:
12 | types: [zenstack-release]
13 |
14 | jobs:
15 | update:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 | - name: Update to latest ZenStack
21 | run: |
22 | git config --global user.name ymc9
23 | git config --global user.email yiming@whimslab.io
24 | npm ci
25 | npm run up
26 |
27 | - name: Build
28 | run: |
29 | npm run build
30 |
31 | - name: Commit and push
32 | run: |
33 | git add .
34 | git commit -m "chore: update to latest ZenStack" || true
35 | git push || true
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 | node_modules/
4 | .env.local
5 | .next
6 | .zenstack_repl_history
7 | .idea
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "useTabs": false,
4 | "printWidth": 120,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Debug server-side",
9 | "type": "node-terminal",
10 | "request": "launch",
11 | "command": "npm run dev"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 ZenStack Repositories
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
11 |
12 | # A Collaborative Todo Sample - ZenStack + Next.js
13 |
14 | This project is a collaborative Todo app built with [Next.js](https://nextjs.org), [Next-Auth](nextauth.org), and [ZenStack](https://zenstack.dev).
15 |
16 | In this fictitious app, users can be invited to workspaces where they can collaborate on todos. Public todo lists are visible to all members in the workspace.
17 |
18 | See a live deployment at: https://zenstack-todo.vercel.app/.
19 |
20 | ## Features
21 |
22 | - User signup/signin
23 | - Creating workspaces and inviting members
24 | - Data segregation and permission control
25 |
26 | ## Implementation
27 |
28 | - Data model is located at `/schema.zmodel`.
29 | - An automatic CRUD API is mounted at `/api/model` by `pages/api/model/[...path].ts`.
30 | - [SWR](https://swr.vercel.app/) CRUD hooks are generated under `lib/hooks` folder.
31 |
32 | ## Running the sample
33 |
34 | 1. Setup a new PostgreSQL database
35 |
36 | You can launch a PostgreSQL instance locally, or create one from a hoster like [Supabase](https://supabase.com). Create a new database for this app, and set the connection string in .env file.
37 |
38 | 1. Install dependencies
39 |
40 | ```bash
41 | npm install
42 | ```
43 |
44 | 1. Generate server and client-side code from model
45 |
46 | ```bash
47 | npm run generate
48 | ```
49 |
50 | 1. Synchronize database schema
51 |
52 | ```bash
53 | npm run db:push
54 | ```
55 |
56 | 1. Start dev server
57 |
58 | ```bash
59 | npm run dev
60 | ```
61 |
62 | For more information on using ZenStack, visit [https://zenstack.dev](https://zenstack.dev).
63 |
--------------------------------------------------------------------------------
/components/AuthGuard.tsx:
--------------------------------------------------------------------------------
1 | import { useSession } from 'next-auth/react';
2 | import { useRouter } from 'next/router';
3 |
4 | type Props = {
5 | children: JSX.Element | JSX.Element[];
6 | };
7 |
8 | export default function AuthGuard({ children }: Props) {
9 | const { status } = useSession();
10 | const router = useRouter();
11 |
12 | if (router.pathname === '/signup' || router.pathname === '/signin') {
13 | return <>{children}>;
14 | }
15 |
16 | if (status === 'loading') {
17 | return Loading...
;
18 | } else if (status === 'unauthenticated') {
19 | void router.push('/signin');
20 | return <>>;
21 | } else {
22 | return <>{children}>;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/components/Avatar.tsx:
--------------------------------------------------------------------------------
1 | import { User } from 'next-auth';
2 | import Image from 'next/image';
3 |
4 | type Props = {
5 | user: User;
6 | size?: number;
7 | };
8 |
9 | export default function Avatar({ user, size }: Props) {
10 | if (!user) {
11 | return <>>;
12 | }
13 | return (
14 |
15 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/components/BreadCrumb.tsx:
--------------------------------------------------------------------------------
1 | import { List, Space } from '@prisma/client';
2 | import Link from 'next/link';
3 | import { useRouter } from 'next/router';
4 |
5 | type Props = {
6 | space: Space;
7 | list?: List;
8 | };
9 |
10 | export default function BreadCrumb({ space, list }: Props) {
11 | const router = useRouter();
12 |
13 | const parts = router.asPath.split('/').filter((p) => p);
14 | const [base] = parts;
15 | if (base !== 'space') {
16 | return <>>;
17 | }
18 |
19 | const items: Array<{ text: string; link: string }> = [];
20 |
21 | items.push({ text: 'Home', link: '/' });
22 | items.push({ text: space.name || '', link: `/space/${space.slug}` });
23 |
24 | if (list) {
25 | items.push({
26 | text: list?.title || '',
27 | link: `/space/${space.slug}/${list.id}`,
28 | });
29 | }
30 |
31 | return (
32 |
33 |
34 | {items.map((item, i) => (
35 |
36 | {item.text}
37 |
38 | ))}
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/components/ManageMembers.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2 | import { PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
3 | import { useCurrentUser } from '@lib/context';
4 | import { useCreateSpaceUser, useDeleteSpaceUser, useFindManySpaceUser } from '@lib/hooks';
5 | import { Space, SpaceUserRole } from '@prisma/client';
6 | import { ChangeEvent, KeyboardEvent, useState } from 'react';
7 | import { toast } from 'react-toastify';
8 | import Avatar from './Avatar';
9 |
10 | type Props = {
11 | space: Space;
12 | };
13 |
14 | export default function ManageMembers({ space }: Props) {
15 | const [email, setEmail] = useState('');
16 | const [role, setRole] = useState(SpaceUserRole.USER);
17 | const user = useCurrentUser();
18 | const { trigger: createSpaceUser } = useCreateSpaceUser();
19 | const { trigger: deleteSpaceUser } = useDeleteSpaceUser();
20 |
21 | const { data: members } = useFindManySpaceUser({
22 | where: {
23 | spaceId: space.id,
24 | },
25 | include: {
26 | user: true,
27 | },
28 | orderBy: {
29 | role: 'desc',
30 | },
31 | });
32 |
33 | const inviteUser = async () => {
34 | try {
35 | const r = await createSpaceUser({
36 | data: {
37 | user: {
38 | connect: {
39 | email,
40 | },
41 | },
42 | space: {
43 | connect: {
44 | id: space.id,
45 | },
46 | },
47 | role,
48 | },
49 | });
50 | console.log('SpaceUser created:', r);
51 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
52 | } catch (err: any) {
53 | console.error(err);
54 | if (err.info?.prisma === true) {
55 | if (err.info.code === 'P2002') {
56 | toast.error('User is already a member of the space');
57 | } else if (err.info.code === 'P2025') {
58 | toast.error('User is not found for this email');
59 | } else {
60 | toast.error(`Unexpected Prisma error: ${err.info.code}`);
61 | }
62 | } else {
63 | toast.error(`Error occurred: ${JSON.stringify(err)}`);
64 | }
65 | }
66 | };
67 |
68 | const removeMember = (id: string) => {
69 | if (confirm(`Are you sure to remove this member from space?`)) {
70 | void deleteSpaceUser({ where: { id } });
71 | }
72 | };
73 |
74 | return (
75 |
132 | );
133 | }
134 |
--------------------------------------------------------------------------------
/components/NavBar.tsx:
--------------------------------------------------------------------------------
1 | import { Space } from '@prisma/client';
2 | import { User } from 'next-auth';
3 | import { signOut } from 'next-auth/react';
4 | import Image from 'next/image';
5 | import Link from 'next/link';
6 | import Avatar from './Avatar';
7 |
8 | type Props = {
9 | space: Space | undefined;
10 | user: User | undefined;
11 | };
12 |
13 | export default function NavBar({ user, space }: Props) {
14 | const onSignout = () => {
15 | void signOut({ callbackUrl: '/signin' });
16 | };
17 |
18 | return (
19 |
20 |
21 |
22 |
23 |
24 | {space?.name || 'Welcome Todo App'}
25 |
26 |
Powered by ZenStack
27 |
28 |
29 |
30 |
31 |
32 | {user && }
33 |
34 |
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/components/SpaceMembers.tsx:
--------------------------------------------------------------------------------
1 | import { PlusIcon } from '@heroicons/react/24/outline';
2 | import { useCurrentSpace } from '@lib/context';
3 | import { useFindManySpaceUser } from '@lib/hooks';
4 | import { Space } from '@prisma/client';
5 | import Avatar from './Avatar';
6 | import ManageMembers from './ManageMembers';
7 |
8 | function ManagementDialog(space?: Space) {
9 | if (!space) return undefined;
10 | return (
11 | <>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Manage Members of {space.name}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Close
28 |
29 |
30 |
31 |
32 | >
33 | );
34 | }
35 |
36 | export default function SpaceMembers() {
37 | const space = useCurrentSpace();
38 |
39 | const { data: members } = useFindManySpaceUser(
40 | {
41 | where: {
42 | spaceId: space?.id,
43 | },
44 | include: {
45 | user: true,
46 | },
47 | orderBy: {
48 | role: 'desc',
49 | },
50 | },
51 | { disabled: !space }
52 | );
53 |
54 | return (
55 |
56 | {ManagementDialog(space)}
57 | {members && (
58 |
59 | {members?.map((member) => (
60 |
61 | ))}
62 |
63 | )}
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/components/Spaces.tsx:
--------------------------------------------------------------------------------
1 | import { useCountList } from '@lib/hooks';
2 | import { Space } from '@prisma/client';
3 | import Link from 'next/link';
4 |
5 | type Props = {
6 | spaces: Space[];
7 | };
8 |
9 | function SpaceItem({ space }: { space: Space }) {
10 | const { data: listCount } = useCountList({
11 | where: { spaceId: space.id },
12 | });
13 | return (
14 |
15 |
{listCount}
16 |
17 |
18 |
{space.name}
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | export default function Spaces({ spaces }: Props) {
26 | return (
27 |
28 | {spaces?.map((space) => (
29 |
33 |
34 |
35 | ))}
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/components/TimeInfo.tsx:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | type Props = {
4 | value: { createdAt: Date; updatedAt: Date; completedAt?: Date | null };
5 | };
6 |
7 | export default function TimeInfo({ value }: Props) {
8 | return (
9 |
10 | {value.completedAt
11 | ? `Completed ${moment(value.completedAt).fromNow()}`
12 | : value.createdAt === value.updatedAt
13 | ? `Created ${moment(value.createdAt).fromNow()}`
14 | : `Updated ${moment(value.updatedAt).fromNow()}`}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/components/Todo.tsx:
--------------------------------------------------------------------------------
1 | import { TrashIcon } from '@heroicons/react/24/outline';
2 | import { useDeleteTodo, useUpdateTodo } from '@lib/hooks';
3 | import { Todo, User } from '@prisma/client';
4 | import { ChangeEvent } from 'react';
5 | import Avatar from './Avatar';
6 | import TimeInfo from './TimeInfo';
7 |
8 | type Props = {
9 | value: Todo & { owner: User };
10 | optimistic?: boolean;
11 | };
12 |
13 | export default function TodoComponent({ value, optimistic }: Props) {
14 | const { trigger: updateTodo } = useUpdateTodo({ optimisticUpdate: true });
15 | const { trigger: deleteTodo } = useDeleteTodo({ optimisticUpdate: true });
16 |
17 | const onDeleteTodo = () => {
18 | void deleteTodo({ where: { id: value.id } });
19 | };
20 |
21 | const toggleCompleted = (completed: boolean) => {
22 | if (completed === !!value.completedAt) {
23 | return;
24 | }
25 | void updateTodo({
26 | where: { id: value.id },
27 | data: { completedAt: completed ? new Date() : null },
28 | });
29 | };
30 |
31 | return (
32 |
33 |
34 |
39 | {value.title}
40 | {optimistic && }
41 |
42 |
43 | ) => toggleCompleted(e.currentTarget.checked)}
49 | />
50 | {
55 | !optimistic && onDeleteTodo();
56 | }}
57 | />
58 |
59 |
60 |
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/components/TodoList.tsx:
--------------------------------------------------------------------------------
1 | import { LockClosedIcon, TrashIcon } from '@heroicons/react/24/outline';
2 | import { useCheckList, useDeleteList } from '@lib/hooks';
3 | import { List } from '@prisma/client';
4 | import { customAlphabet } from 'nanoid';
5 | import { User } from 'next-auth';
6 | import Image from 'next/image';
7 | import Link from 'next/link';
8 | import { useRouter } from 'next/router';
9 | import Avatar from './Avatar';
10 | import TimeInfo from './TimeInfo';
11 |
12 | type Props = {
13 | value: List & { owner: User };
14 | deleted?: (value: List) => void;
15 | };
16 |
17 | export default function TodoList({ value }: Props) {
18 | const router = useRouter();
19 |
20 | // check if the current user can delete the list (based on its owner)
21 | const { data: canDelete } = useCheckList({ operation: 'delete', where: { ownerId: value.ownerId } });
22 |
23 | const { trigger: deleteList } = useDeleteList();
24 |
25 | const onDeleteList = () => {
26 | if (confirm('Are you sure to delete this list?')) {
27 | void deleteList({ where: { id: value.id } });
28 | }
29 | };
30 |
31 | return (
32 |
33 |
34 |
35 |
42 |
43 |
44 |
45 |
46 |
{value.title || 'Missing Title'}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {value.private && (
55 |
56 |
57 |
58 | )}
59 |
60 | {canDelete && (
61 |
{
64 | onDeleteList();
65 | }}
66 | />
67 | )}
68 |
69 |
70 |
71 |
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/components/WithNavBar.tsx:
--------------------------------------------------------------------------------
1 | import { useCurrentSpace, useCurrentUser } from '@lib/context';
2 | import NavBar from './NavBar';
3 |
4 | type Props = {
5 | children: JSX.Element | JSX.Element[] | undefined;
6 | };
7 |
8 | export default function WithNavBar({ children }: Props) {
9 | const user = useCurrentUser();
10 | const space = useCurrentSpace();
11 |
12 | return (
13 | <>
14 |
15 | {children}
16 | >
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/lib/context.ts:
--------------------------------------------------------------------------------
1 | import { Space } from '@prisma/client';
2 | import { User } from 'next-auth';
3 | import { useSession } from 'next-auth/react';
4 | import { useRouter } from 'next/router';
5 | import { createContext } from 'react';
6 | import { useFindManySpace } from './hooks';
7 |
8 | export const UserContext = createContext(undefined);
9 |
10 | export function useCurrentUser() {
11 | const { data: session } = useSession();
12 | return session?.user;
13 | }
14 |
15 | export const SpaceContext = createContext(undefined);
16 |
17 | export function useCurrentSpace() {
18 | const router = useRouter();
19 | const { data: spaces } = useFindManySpace(
20 | {
21 | where: {
22 | slug: router.query.slug as string,
23 | },
24 | },
25 | {
26 | disabled: !router.query.slug,
27 | }
28 | );
29 |
30 | return spaces?.[0];
31 | }
32 |
--------------------------------------------------------------------------------
/lib/hooks/__model_meta.ts:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * This file was generated by ZenStack CLI.
3 | ******************************************************************************/
4 |
5 | /* eslint-disable */
6 | // @ts-nocheck
7 |
8 | const metadata = {
9 | models: {
10 | space: {
11 | name: 'Space', fields: {
12 | id: {
13 | name: "id",
14 | type: "String",
15 | isId: true,
16 | attributes: [{ "name": "@default", "args": [] }],
17 | }, createdAt: {
18 | name: "createdAt",
19 | type: "DateTime",
20 | attributes: [{ "name": "@default", "args": [] }],
21 | }, updatedAt: {
22 | name: "updatedAt",
23 | type: "DateTime",
24 | attributes: [{ "name": "@updatedAt", "args": [] }],
25 | }, owner: {
26 | name: "owner",
27 | type: "User",
28 | isDataModel: true,
29 | backLink: 'ownedSpaces',
30 | isRelationOwner: true,
31 | onDeleteAction: 'Cascade',
32 | foreignKeyMapping: { "id": "ownerId" },
33 | }, ownerId: {
34 | name: "ownerId",
35 | type: "String",
36 | attributes: [{ "name": "@default", "args": [] }],
37 | defaultValueProvider: $default$Space$ownerId,
38 | isForeignKey: true,
39 | relationField: 'owner',
40 | }, name: {
41 | name: "name",
42 | type: "String",
43 | }, slug: {
44 | name: "slug",
45 | type: "String",
46 | }, members: {
47 | name: "members",
48 | type: "SpaceUser",
49 | isDataModel: true,
50 | isArray: true,
51 | backLink: 'space',
52 | }, lists: {
53 | name: "lists",
54 | type: "List",
55 | isDataModel: true,
56 | isArray: true,
57 | backLink: 'space',
58 | },
59 | }, uniqueConstraints: {
60 | id: {
61 | name: "id",
62 | fields: ["id"]
63 | }, slug: {
64 | name: "slug",
65 | fields: ["slug"]
66 | },
67 | },
68 | },
69 | spaceUser: {
70 | name: 'SpaceUser', fields: {
71 | id: {
72 | name: "id",
73 | type: "String",
74 | isId: true,
75 | attributes: [{ "name": "@default", "args": [] }],
76 | }, createdAt: {
77 | name: "createdAt",
78 | type: "DateTime",
79 | attributes: [{ "name": "@default", "args": [] }],
80 | }, updatedAt: {
81 | name: "updatedAt",
82 | type: "DateTime",
83 | attributes: [{ "name": "@updatedAt", "args": [] }],
84 | }, space: {
85 | name: "space",
86 | type: "Space",
87 | isDataModel: true,
88 | backLink: 'members',
89 | isRelationOwner: true,
90 | onDeleteAction: 'Cascade',
91 | foreignKeyMapping: { "id": "spaceId" },
92 | }, spaceId: {
93 | name: "spaceId",
94 | type: "String",
95 | isForeignKey: true,
96 | relationField: 'space',
97 | }, user: {
98 | name: "user",
99 | type: "User",
100 | isDataModel: true,
101 | backLink: 'memberships',
102 | isRelationOwner: true,
103 | onDeleteAction: 'Cascade',
104 | foreignKeyMapping: { "id": "userId" },
105 | }, userId: {
106 | name: "userId",
107 | type: "String",
108 | isForeignKey: true,
109 | relationField: 'user',
110 | }, role: {
111 | name: "role",
112 | type: "SpaceUserRole",
113 | },
114 | }, uniqueConstraints: {
115 | id: {
116 | name: "id",
117 | fields: ["id"]
118 | }, userId_spaceId: {
119 | name: "userId_spaceId",
120 | fields: ["userId", "spaceId"]
121 | },
122 | },
123 | },
124 | user: {
125 | name: 'User', fields: {
126 | id: {
127 | name: "id",
128 | type: "String",
129 | isId: true,
130 | attributes: [{ "name": "@default", "args": [] }],
131 | }, createdAt: {
132 | name: "createdAt",
133 | type: "DateTime",
134 | attributes: [{ "name": "@default", "args": [] }],
135 | }, updatedAt: {
136 | name: "updatedAt",
137 | type: "DateTime",
138 | attributes: [{ "name": "@updatedAt", "args": [] }],
139 | }, email: {
140 | name: "email",
141 | type: "String",
142 | }, emailVerified: {
143 | name: "emailVerified",
144 | type: "DateTime",
145 | isOptional: true,
146 | }, password: {
147 | name: "password",
148 | type: "String",
149 | isOptional: true,
150 | }, name: {
151 | name: "name",
152 | type: "String",
153 | isOptional: true,
154 | }, ownedSpaces: {
155 | name: "ownedSpaces",
156 | type: "Space",
157 | isDataModel: true,
158 | isArray: true,
159 | backLink: 'owner',
160 | }, memberships: {
161 | name: "memberships",
162 | type: "SpaceUser",
163 | isDataModel: true,
164 | isArray: true,
165 | backLink: 'user',
166 | }, image: {
167 | name: "image",
168 | type: "String",
169 | isOptional: true,
170 | }, lists: {
171 | name: "lists",
172 | type: "List",
173 | isDataModel: true,
174 | isArray: true,
175 | backLink: 'owner',
176 | }, todos: {
177 | name: "todos",
178 | type: "Todo",
179 | isDataModel: true,
180 | isArray: true,
181 | backLink: 'owner',
182 | }, accounts: {
183 | name: "accounts",
184 | type: "Account",
185 | isDataModel: true,
186 | isArray: true,
187 | backLink: 'user',
188 | },
189 | }, uniqueConstraints: {
190 | id: {
191 | name: "id",
192 | fields: ["id"]
193 | }, email: {
194 | name: "email",
195 | fields: ["email"]
196 | },
197 | },
198 | },
199 | list: {
200 | name: 'List', fields: {
201 | id: {
202 | name: "id",
203 | type: "String",
204 | isId: true,
205 | attributes: [{ "name": "@default", "args": [] }],
206 | }, createdAt: {
207 | name: "createdAt",
208 | type: "DateTime",
209 | attributes: [{ "name": "@default", "args": [] }],
210 | }, updatedAt: {
211 | name: "updatedAt",
212 | type: "DateTime",
213 | attributes: [{ "name": "@updatedAt", "args": [] }],
214 | }, space: {
215 | name: "space",
216 | type: "Space",
217 | isDataModel: true,
218 | backLink: 'lists',
219 | isRelationOwner: true,
220 | onDeleteAction: 'Cascade',
221 | foreignKeyMapping: { "id": "spaceId" },
222 | }, spaceId: {
223 | name: "spaceId",
224 | type: "String",
225 | isForeignKey: true,
226 | relationField: 'space',
227 | }, owner: {
228 | name: "owner",
229 | type: "User",
230 | isDataModel: true,
231 | backLink: 'lists',
232 | isRelationOwner: true,
233 | onDeleteAction: 'Cascade',
234 | foreignKeyMapping: { "id": "ownerId" },
235 | }, ownerId: {
236 | name: "ownerId",
237 | type: "String",
238 | attributes: [{ "name": "@default", "args": [] }],
239 | defaultValueProvider: $default$List$ownerId,
240 | isForeignKey: true,
241 | relationField: 'owner',
242 | }, title: {
243 | name: "title",
244 | type: "String",
245 | }, private: {
246 | name: "private",
247 | type: "Boolean",
248 | attributes: [{ "name": "@default", "args": [{ "value": false }] }],
249 | }, todos: {
250 | name: "todos",
251 | type: "Todo",
252 | isDataModel: true,
253 | isArray: true,
254 | backLink: 'list',
255 | },
256 | }, uniqueConstraints: {
257 | id: {
258 | name: "id",
259 | fields: ["id"]
260 | },
261 | },
262 | },
263 | todo: {
264 | name: 'Todo', fields: {
265 | id: {
266 | name: "id",
267 | type: "String",
268 | isId: true,
269 | attributes: [{ "name": "@default", "args": [] }],
270 | }, createdAt: {
271 | name: "createdAt",
272 | type: "DateTime",
273 | attributes: [{ "name": "@default", "args": [] }],
274 | }, updatedAt: {
275 | name: "updatedAt",
276 | type: "DateTime",
277 | attributes: [{ "name": "@updatedAt", "args": [] }],
278 | }, owner: {
279 | name: "owner",
280 | type: "User",
281 | isDataModel: true,
282 | backLink: 'todos',
283 | isRelationOwner: true,
284 | onDeleteAction: 'Cascade',
285 | foreignKeyMapping: { "id": "ownerId" },
286 | }, ownerId: {
287 | name: "ownerId",
288 | type: "String",
289 | attributes: [{ "name": "@default", "args": [] }],
290 | defaultValueProvider: $default$Todo$ownerId,
291 | isForeignKey: true,
292 | relationField: 'owner',
293 | }, list: {
294 | name: "list",
295 | type: "List",
296 | isDataModel: true,
297 | backLink: 'todos',
298 | isRelationOwner: true,
299 | onDeleteAction: 'Cascade',
300 | foreignKeyMapping: { "id": "listId" },
301 | }, listId: {
302 | name: "listId",
303 | type: "String",
304 | isForeignKey: true,
305 | relationField: 'list',
306 | }, title: {
307 | name: "title",
308 | type: "String",
309 | }, completedAt: {
310 | name: "completedAt",
311 | type: "DateTime",
312 | isOptional: true,
313 | },
314 | }, uniqueConstraints: {
315 | id: {
316 | name: "id",
317 | fields: ["id"]
318 | },
319 | },
320 | },
321 | account: {
322 | name: 'Account', fields: {
323 | id: {
324 | name: "id",
325 | type: "String",
326 | isId: true,
327 | attributes: [{ "name": "@default", "args": [] }],
328 | }, userId: {
329 | name: "userId",
330 | type: "String",
331 | isForeignKey: true,
332 | relationField: 'user',
333 | }, type: {
334 | name: "type",
335 | type: "String",
336 | }, provider: {
337 | name: "provider",
338 | type: "String",
339 | }, providerAccountId: {
340 | name: "providerAccountId",
341 | type: "String",
342 | }, refresh_token: {
343 | name: "refresh_token",
344 | type: "String",
345 | isOptional: true,
346 | }, refresh_token_expires_in: {
347 | name: "refresh_token_expires_in",
348 | type: "Int",
349 | isOptional: true,
350 | }, access_token: {
351 | name: "access_token",
352 | type: "String",
353 | isOptional: true,
354 | }, expires_at: {
355 | name: "expires_at",
356 | type: "Int",
357 | isOptional: true,
358 | }, token_type: {
359 | name: "token_type",
360 | type: "String",
361 | isOptional: true,
362 | }, scope: {
363 | name: "scope",
364 | type: "String",
365 | isOptional: true,
366 | }, id_token: {
367 | name: "id_token",
368 | type: "String",
369 | isOptional: true,
370 | }, session_state: {
371 | name: "session_state",
372 | type: "String",
373 | isOptional: true,
374 | }, user: {
375 | name: "user",
376 | type: "User",
377 | isDataModel: true,
378 | backLink: 'accounts',
379 | isRelationOwner: true,
380 | onDeleteAction: 'Cascade',
381 | foreignKeyMapping: { "id": "userId" },
382 | },
383 | }, uniqueConstraints: {
384 | id: {
385 | name: "id",
386 | fields: ["id"]
387 | }, provider_providerAccountId: {
388 | name: "provider_providerAccountId",
389 | fields: ["provider", "providerAccountId"]
390 | },
391 | },
392 | },
393 |
394 | },
395 | deleteCascade: {
396 | space: ['SpaceUser', 'List'],
397 | user: ['Space', 'SpaceUser', 'List', 'Todo', 'Account'],
398 | list: ['Todo'],
399 |
400 | },
401 | authModel: 'User'
402 |
403 | };
404 |
405 | function $default$Space$ownerId(user: any): unknown {
406 | return user?.id;
407 | }
408 |
409 | function $default$List$ownerId(user: any): unknown {
410 | return user?.id;
411 | }
412 |
413 | function $default$Todo$ownerId(user: any): unknown {
414 | return user?.id;
415 | }
416 | export default metadata;
417 |
--------------------------------------------------------------------------------
/lib/hooks/account.ts:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * This file was generated by ZenStack CLI.
3 | ******************************************************************************/
4 |
5 | /* eslint-disable */
6 | // @ts-nocheck
7 |
8 | import type { Prisma } from "@zenstackhq/runtime/models";
9 | import { type GetNextArgs, type QueryOptions, type InfiniteQueryOptions, type MutationOptions, type PickEnumerable } from '@zenstackhq/swr/runtime';
10 | import type { PolicyCrudKind } from '@zenstackhq/runtime'
11 | import metadata from './__model_meta';
12 | import * as request from '@zenstackhq/swr/runtime';
13 |
14 | export function useCreateAccount(options?: MutationOptions | undefined, unknown, Prisma.AccountCreateArgs>) {
15 | const mutation = request.useModelMutation('Account', 'POST', 'create', metadata, options, true);
16 | return {
17 | ...mutation,
18 | trigger: (args: Prisma.SelectSubset) => {
19 | return mutation.trigger(args, options as any) as Promise | undefined>;
20 | }
21 | };
22 | }
23 |
24 | export function useCreateManyAccount(options?: MutationOptions) {
25 | const mutation = request.useModelMutation('Account', 'POST', 'createMany', metadata, options, false);
26 | return {
27 | ...mutation,
28 | trigger: (args: Prisma.SelectSubset) => {
29 | return mutation.trigger(args, options as any) as Promise;
30 | }
31 | };
32 | }
33 |
34 | export function useFindManyAccount(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>>) {
35 | return request.useModelQuery('Account', 'findMany', args, options);
36 | }
37 |
38 | export function useInfiniteFindManyAccount>>(getNextArgs: GetNextArgs | undefined, R>, options?: InfiniteQueryOptions>>) {
39 | return request.useInfiniteModelQuery('Account', 'findMany', getNextArgs, options);
40 | }
41 |
42 | export function useFindUniqueAccount(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
43 | return request.useModelQuery('Account', 'findUnique', args, options);
44 | }
45 |
46 | export function useFindFirstAccount(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
47 | return request.useModelQuery('Account', 'findFirst', args, options);
48 | }
49 |
50 | export function useUpdateAccount(options?: MutationOptions | undefined, unknown, Prisma.AccountUpdateArgs>) {
51 | const mutation = request.useModelMutation('Account', 'PUT', 'update', metadata, options, true);
52 | return {
53 | ...mutation,
54 | trigger: (args: Prisma.SelectSubset) => {
55 | return mutation.trigger(args, options as any) as Promise | undefined>;
56 | }
57 | };
58 | }
59 |
60 | export function useUpdateManyAccount(options?: MutationOptions) {
61 | const mutation = request.useModelMutation('Account', 'PUT', 'updateMany', metadata, options, false);
62 | return {
63 | ...mutation,
64 | trigger: (args: Prisma.SelectSubset) => {
65 | return mutation.trigger(args, options as any) as Promise;
66 | }
67 | };
68 | }
69 |
70 | export function useUpsertAccount(options?: MutationOptions | undefined, unknown, Prisma.AccountUpsertArgs>) {
71 | const mutation = request.useModelMutation('Account', 'POST', 'upsert', metadata, options, true);
72 | return {
73 | ...mutation,
74 | trigger: (args: Prisma.SelectSubset) => {
75 | return mutation.trigger(args, options as any) as Promise | undefined>;
76 | }
77 | };
78 | }
79 |
80 | export function useDeleteAccount(options?: MutationOptions | undefined, unknown, Prisma.AccountDeleteArgs>) {
81 | const mutation = request.useModelMutation('Account', 'DELETE', 'delete', metadata, options, true);
82 | return {
83 | ...mutation,
84 | trigger: (args: Prisma.SelectSubset) => {
85 | return mutation.trigger(args, options as any) as Promise | undefined>;
86 | }
87 | };
88 | }
89 |
90 | export function useDeleteManyAccount(options?: MutationOptions) {
91 | const mutation = request.useModelMutation('Account', 'DELETE', 'deleteMany', metadata, options, false);
92 | return {
93 | ...mutation,
94 | trigger: (args: Prisma.SelectSubset) => {
95 | return mutation.trigger(args, options as any) as Promise;
96 | }
97 | };
98 | }
99 |
100 | export function useAggregateAccount(args?: Prisma.Subset, options?: QueryOptions>) {
101 | return request.useModelQuery('Account', 'aggregate', args, options);
102 | }
103 |
104 | export function useGroupByAccount>, Prisma.Extends<'take', Prisma.Keys>>, OrderByArg extends Prisma.True extends HasSelectOrTake ? { orderBy: Prisma.AccountGroupByArgs['orderBy'] } : { orderBy?: Prisma.AccountGroupByArgs['orderBy'] }, OrderFields extends Prisma.ExcludeUnderscoreKeys>>, ByFields extends Prisma.MaybeTupleToUnion, ByValid extends Prisma.Has, HavingFields extends Prisma.GetHavingFields, HavingValid extends Prisma.Has, ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, InputErrors extends ByEmpty extends Prisma.True
105 | ? `Error: "by" must not be empty.`
106 | : HavingValid extends Prisma.False
107 | ? {
108 | [P in HavingFields]: P extends ByFields
109 | ? never
110 | : P extends string
111 | ? `Error: Field "${P}" used in "having" needs to be provided in "by".`
112 | : [
113 | Error,
114 | 'Field ',
115 | P,
116 | ` in "having" needs to be provided in "by"`,
117 | ]
118 | }[HavingFields]
119 | : 'take' extends Prisma.Keys
120 | ? 'orderBy' extends Prisma.Keys
121 | ? ByValid extends Prisma.True
122 | ? {}
123 | : {
124 | [P in OrderFields]: P extends ByFields
125 | ? never
126 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
127 | }[OrderFields]
128 | : 'Error: If you provide "take", you also need to provide "orderBy"'
129 | : 'skip' extends Prisma.Keys
130 | ? 'orderBy' extends Prisma.Keys
131 | ? ByValid extends Prisma.True
132 | ? {}
133 | : {
134 | [P in OrderFields]: P extends ByFields
135 | ? never
136 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
137 | }[OrderFields]
138 | : 'Error: If you provide "skip", you also need to provide "orderBy"'
139 | : ByValid extends Prisma.True
140 | ? {}
141 | : {
142 | [P in OrderFields]: P extends ByFields
143 | ? never
144 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
145 | }[OrderFields]>(args?: Prisma.SubsetIntersection & InputErrors, options?: QueryOptions<{} extends InputErrors ?
146 | Array &
147 | {
148 | [P in ((keyof T) & (keyof Prisma.AccountGroupByOutputType))]: P extends '_count'
149 | ? T[P] extends boolean
150 | ? number
151 | : Prisma.GetScalarType
152 | : Prisma.GetScalarType
153 | }
154 | > : InputErrors>) {
155 | return request.useModelQuery('Account', 'groupBy', args, options);
156 | }
157 |
158 | export function useCountAccount(args?: Prisma.Subset, options?: QueryOptions : number>) {
159 | return request.useModelQuery('Account', 'count', args, options);
160 | }
161 |
162 | export function useCheckAccount(args: { operation: PolicyCrudKind; where?: { id?: string; userId?: string; type?: string; provider?: string; providerAccountId?: string; refresh_token?: string; refresh_token_expires_in?: number; access_token?: string; expires_at?: number; token_type?: string; scope?: string; id_token?: string; session_state?: string }; }, options?: QueryOptions) {
163 | return request.useModelQuery('Account', 'check', args, options);
164 | }
165 |
--------------------------------------------------------------------------------
/lib/hooks/index.ts:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * This file was generated by ZenStack CLI.
3 | ******************************************************************************/
4 |
5 | /* eslint-disable */
6 | // @ts-nocheck
7 |
8 | export * from './space';
9 | export * from './space-user';
10 | export * from './user';
11 | export * from './list';
12 | export * from './todo';
13 | export * from './account';
14 | export { Provider } from '@zenstackhq/swr/runtime';
15 | export { default as metadata } from './__model_meta';
16 |
--------------------------------------------------------------------------------
/lib/hooks/list.ts:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * This file was generated by ZenStack CLI.
3 | ******************************************************************************/
4 |
5 | /* eslint-disable */
6 | // @ts-nocheck
7 |
8 | import type { Prisma } from "@zenstackhq/runtime/models";
9 | import { type GetNextArgs, type QueryOptions, type InfiniteQueryOptions, type MutationOptions, type PickEnumerable } from '@zenstackhq/swr/runtime';
10 | import type { PolicyCrudKind } from '@zenstackhq/runtime'
11 | import metadata from './__model_meta';
12 | import * as request from '@zenstackhq/swr/runtime';
13 |
14 | export function useCreateList(options?: MutationOptions | undefined, unknown, Prisma.ListCreateArgs>) {
15 | const mutation = request.useModelMutation('List', 'POST', 'create', metadata, options, true);
16 | return {
17 | ...mutation,
18 | trigger: (args: Prisma.SelectSubset) => {
19 | return mutation.trigger(args, options as any) as Promise | undefined>;
20 | }
21 | };
22 | }
23 |
24 | export function useCreateManyList(options?: MutationOptions) {
25 | const mutation = request.useModelMutation('List', 'POST', 'createMany', metadata, options, false);
26 | return {
27 | ...mutation,
28 | trigger: (args: Prisma.SelectSubset) => {
29 | return mutation.trigger(args, options as any) as Promise;
30 | }
31 | };
32 | }
33 |
34 | export function useFindManyList(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>>) {
35 | return request.useModelQuery('List', 'findMany', args, options);
36 | }
37 |
38 | export function useInfiniteFindManyList>>(getNextArgs: GetNextArgs | undefined, R>, options?: InfiniteQueryOptions>>) {
39 | return request.useInfiniteModelQuery('List', 'findMany', getNextArgs, options);
40 | }
41 |
42 | export function useFindUniqueList(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
43 | return request.useModelQuery('List', 'findUnique', args, options);
44 | }
45 |
46 | export function useFindFirstList(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
47 | return request.useModelQuery('List', 'findFirst', args, options);
48 | }
49 |
50 | export function useUpdateList(options?: MutationOptions | undefined, unknown, Prisma.ListUpdateArgs>) {
51 | const mutation = request.useModelMutation('List', 'PUT', 'update', metadata, options, true);
52 | return {
53 | ...mutation,
54 | trigger: (args: Prisma.SelectSubset) => {
55 | return mutation.trigger(args, options as any) as Promise | undefined>;
56 | }
57 | };
58 | }
59 |
60 | export function useUpdateManyList(options?: MutationOptions) {
61 | const mutation = request.useModelMutation('List', 'PUT', 'updateMany', metadata, options, false);
62 | return {
63 | ...mutation,
64 | trigger: (args: Prisma.SelectSubset) => {
65 | return mutation.trigger(args, options as any) as Promise;
66 | }
67 | };
68 | }
69 |
70 | export function useUpsertList(options?: MutationOptions | undefined, unknown, Prisma.ListUpsertArgs>) {
71 | const mutation = request.useModelMutation('List', 'POST', 'upsert', metadata, options, true);
72 | return {
73 | ...mutation,
74 | trigger: (args: Prisma.SelectSubset) => {
75 | return mutation.trigger(args, options as any) as Promise | undefined>;
76 | }
77 | };
78 | }
79 |
80 | export function useDeleteList(options?: MutationOptions | undefined, unknown, Prisma.ListDeleteArgs>) {
81 | const mutation = request.useModelMutation('List', 'DELETE', 'delete', metadata, options, true);
82 | return {
83 | ...mutation,
84 | trigger: (args: Prisma.SelectSubset) => {
85 | return mutation.trigger(args, options as any) as Promise | undefined>;
86 | }
87 | };
88 | }
89 |
90 | export function useDeleteManyList(options?: MutationOptions) {
91 | const mutation = request.useModelMutation('List', 'DELETE', 'deleteMany', metadata, options, false);
92 | return {
93 | ...mutation,
94 | trigger: (args: Prisma.SelectSubset) => {
95 | return mutation.trigger(args, options as any) as Promise;
96 | }
97 | };
98 | }
99 |
100 | export function useAggregateList(args?: Prisma.Subset, options?: QueryOptions>) {
101 | return request.useModelQuery('List', 'aggregate', args, options);
102 | }
103 |
104 | export function useGroupByList>, Prisma.Extends<'take', Prisma.Keys>>, OrderByArg extends Prisma.True extends HasSelectOrTake ? { orderBy: Prisma.ListGroupByArgs['orderBy'] } : { orderBy?: Prisma.ListGroupByArgs['orderBy'] }, OrderFields extends Prisma.ExcludeUnderscoreKeys>>, ByFields extends Prisma.MaybeTupleToUnion, ByValid extends Prisma.Has, HavingFields extends Prisma.GetHavingFields, HavingValid extends Prisma.Has, ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, InputErrors extends ByEmpty extends Prisma.True
105 | ? `Error: "by" must not be empty.`
106 | : HavingValid extends Prisma.False
107 | ? {
108 | [P in HavingFields]: P extends ByFields
109 | ? never
110 | : P extends string
111 | ? `Error: Field "${P}" used in "having" needs to be provided in "by".`
112 | : [
113 | Error,
114 | 'Field ',
115 | P,
116 | ` in "having" needs to be provided in "by"`,
117 | ]
118 | }[HavingFields]
119 | : 'take' extends Prisma.Keys
120 | ? 'orderBy' extends Prisma.Keys
121 | ? ByValid extends Prisma.True
122 | ? {}
123 | : {
124 | [P in OrderFields]: P extends ByFields
125 | ? never
126 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
127 | }[OrderFields]
128 | : 'Error: If you provide "take", you also need to provide "orderBy"'
129 | : 'skip' extends Prisma.Keys
130 | ? 'orderBy' extends Prisma.Keys
131 | ? ByValid extends Prisma.True
132 | ? {}
133 | : {
134 | [P in OrderFields]: P extends ByFields
135 | ? never
136 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
137 | }[OrderFields]
138 | : 'Error: If you provide "skip", you also need to provide "orderBy"'
139 | : ByValid extends Prisma.True
140 | ? {}
141 | : {
142 | [P in OrderFields]: P extends ByFields
143 | ? never
144 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
145 | }[OrderFields]>(args?: Prisma.SubsetIntersection & InputErrors, options?: QueryOptions<{} extends InputErrors ?
146 | Array &
147 | {
148 | [P in ((keyof T) & (keyof Prisma.ListGroupByOutputType))]: P extends '_count'
149 | ? T[P] extends boolean
150 | ? number
151 | : Prisma.GetScalarType
152 | : Prisma.GetScalarType
153 | }
154 | > : InputErrors>) {
155 | return request.useModelQuery('List', 'groupBy', args, options);
156 | }
157 |
158 | export function useCountList(args?: Prisma.Subset, options?: QueryOptions : number>) {
159 | return request.useModelQuery('List', 'count', args, options);
160 | }
161 |
162 | export function useCheckList(args: { operation: PolicyCrudKind; where?: { id?: string; spaceId?: string; ownerId?: string; title?: string; private?: boolean }; }, options?: QueryOptions) {
163 | return request.useModelQuery('List', 'check', args, options);
164 | }
165 |
--------------------------------------------------------------------------------
/lib/hooks/space-user.ts:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * This file was generated by ZenStack CLI.
3 | ******************************************************************************/
4 |
5 | /* eslint-disable */
6 | // @ts-nocheck
7 |
8 | import type { Prisma } from "@zenstackhq/runtime/models";
9 | import { type GetNextArgs, type QueryOptions, type InfiniteQueryOptions, type MutationOptions, type PickEnumerable } from '@zenstackhq/swr/runtime';
10 | import type { PolicyCrudKind } from '@zenstackhq/runtime'
11 | import metadata from './__model_meta';
12 | import * as request from '@zenstackhq/swr/runtime';
13 |
14 | export function useCreateSpaceUser(options?: MutationOptions | undefined, unknown, Prisma.SpaceUserCreateArgs>) {
15 | const mutation = request.useModelMutation('SpaceUser', 'POST', 'create', metadata, options, true);
16 | return {
17 | ...mutation,
18 | trigger: (args: Prisma.SelectSubset) => {
19 | return mutation.trigger(args, options as any) as Promise | undefined>;
20 | }
21 | };
22 | }
23 |
24 | export function useCreateManySpaceUser(options?: MutationOptions) {
25 | const mutation = request.useModelMutation('SpaceUser', 'POST', 'createMany', metadata, options, false);
26 | return {
27 | ...mutation,
28 | trigger: (args: Prisma.SelectSubset) => {
29 | return mutation.trigger(args, options as any) as Promise;
30 | }
31 | };
32 | }
33 |
34 | export function useFindManySpaceUser(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>>) {
35 | return request.useModelQuery('SpaceUser', 'findMany', args, options);
36 | }
37 |
38 | export function useInfiniteFindManySpaceUser>>(getNextArgs: GetNextArgs | undefined, R>, options?: InfiniteQueryOptions>>) {
39 | return request.useInfiniteModelQuery('SpaceUser', 'findMany', getNextArgs, options);
40 | }
41 |
42 | export function useFindUniqueSpaceUser(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
43 | return request.useModelQuery('SpaceUser', 'findUnique', args, options);
44 | }
45 |
46 | export function useFindFirstSpaceUser(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
47 | return request.useModelQuery('SpaceUser', 'findFirst', args, options);
48 | }
49 |
50 | export function useUpdateSpaceUser(options?: MutationOptions | undefined, unknown, Prisma.SpaceUserUpdateArgs>) {
51 | const mutation = request.useModelMutation('SpaceUser', 'PUT', 'update', metadata, options, true);
52 | return {
53 | ...mutation,
54 | trigger: (args: Prisma.SelectSubset) => {
55 | return mutation.trigger(args, options as any) as Promise | undefined>;
56 | }
57 | };
58 | }
59 |
60 | export function useUpdateManySpaceUser(options?: MutationOptions) {
61 | const mutation = request.useModelMutation('SpaceUser', 'PUT', 'updateMany', metadata, options, false);
62 | return {
63 | ...mutation,
64 | trigger: (args: Prisma.SelectSubset) => {
65 | return mutation.trigger(args, options as any) as Promise;
66 | }
67 | };
68 | }
69 |
70 | export function useUpsertSpaceUser(options?: MutationOptions | undefined, unknown, Prisma.SpaceUserUpsertArgs>) {
71 | const mutation = request.useModelMutation('SpaceUser', 'POST', 'upsert', metadata, options, true);
72 | return {
73 | ...mutation,
74 | trigger: (args: Prisma.SelectSubset) => {
75 | return mutation.trigger(args, options as any) as Promise | undefined>;
76 | }
77 | };
78 | }
79 |
80 | export function useDeleteSpaceUser(options?: MutationOptions | undefined, unknown, Prisma.SpaceUserDeleteArgs>) {
81 | const mutation = request.useModelMutation('SpaceUser', 'DELETE', 'delete', metadata, options, true);
82 | return {
83 | ...mutation,
84 | trigger: (args: Prisma.SelectSubset) => {
85 | return mutation.trigger(args, options as any) as Promise | undefined>;
86 | }
87 | };
88 | }
89 |
90 | export function useDeleteManySpaceUser(options?: MutationOptions) {
91 | const mutation = request.useModelMutation('SpaceUser', 'DELETE', 'deleteMany', metadata, options, false);
92 | return {
93 | ...mutation,
94 | trigger: (args: Prisma.SelectSubset) => {
95 | return mutation.trigger(args, options as any) as Promise;
96 | }
97 | };
98 | }
99 |
100 | export function useAggregateSpaceUser(args?: Prisma.Subset, options?: QueryOptions>) {
101 | return request.useModelQuery('SpaceUser', 'aggregate', args, options);
102 | }
103 |
104 | export function useGroupBySpaceUser>, Prisma.Extends<'take', Prisma.Keys>>, OrderByArg extends Prisma.True extends HasSelectOrTake ? { orderBy: Prisma.SpaceUserGroupByArgs['orderBy'] } : { orderBy?: Prisma.SpaceUserGroupByArgs['orderBy'] }, OrderFields extends Prisma.ExcludeUnderscoreKeys>>, ByFields extends Prisma.MaybeTupleToUnion, ByValid extends Prisma.Has, HavingFields extends Prisma.GetHavingFields, HavingValid extends Prisma.Has, ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, InputErrors extends ByEmpty extends Prisma.True
105 | ? `Error: "by" must not be empty.`
106 | : HavingValid extends Prisma.False
107 | ? {
108 | [P in HavingFields]: P extends ByFields
109 | ? never
110 | : P extends string
111 | ? `Error: Field "${P}" used in "having" needs to be provided in "by".`
112 | : [
113 | Error,
114 | 'Field ',
115 | P,
116 | ` in "having" needs to be provided in "by"`,
117 | ]
118 | }[HavingFields]
119 | : 'take' extends Prisma.Keys
120 | ? 'orderBy' extends Prisma.Keys
121 | ? ByValid extends Prisma.True
122 | ? {}
123 | : {
124 | [P in OrderFields]: P extends ByFields
125 | ? never
126 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
127 | }[OrderFields]
128 | : 'Error: If you provide "take", you also need to provide "orderBy"'
129 | : 'skip' extends Prisma.Keys
130 | ? 'orderBy' extends Prisma.Keys
131 | ? ByValid extends Prisma.True
132 | ? {}
133 | : {
134 | [P in OrderFields]: P extends ByFields
135 | ? never
136 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
137 | }[OrderFields]
138 | : 'Error: If you provide "skip", you also need to provide "orderBy"'
139 | : ByValid extends Prisma.True
140 | ? {}
141 | : {
142 | [P in OrderFields]: P extends ByFields
143 | ? never
144 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
145 | }[OrderFields]>(args?: Prisma.SubsetIntersection & InputErrors, options?: QueryOptions<{} extends InputErrors ?
146 | Array &
147 | {
148 | [P in ((keyof T) & (keyof Prisma.SpaceUserGroupByOutputType))]: P extends '_count'
149 | ? T[P] extends boolean
150 | ? number
151 | : Prisma.GetScalarType
152 | : Prisma.GetScalarType
153 | }
154 | > : InputErrors>) {
155 | return request.useModelQuery('SpaceUser', 'groupBy', args, options);
156 | }
157 |
158 | export function useCountSpaceUser(args?: Prisma.Subset, options?: QueryOptions : number>) {
159 | return request.useModelQuery('SpaceUser', 'count', args, options);
160 | }
161 | import type { SpaceUserRole } from '@zenstackhq/runtime/models';
162 |
163 | export function useCheckSpaceUser(args: { operation: PolicyCrudKind; where?: { id?: string; spaceId?: string; userId?: string; role?: SpaceUserRole }; }, options?: QueryOptions) {
164 | return request.useModelQuery('SpaceUser', 'check', args, options);
165 | }
166 |
--------------------------------------------------------------------------------
/lib/hooks/space.ts:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * This file was generated by ZenStack CLI.
3 | ******************************************************************************/
4 |
5 | /* eslint-disable */
6 | // @ts-nocheck
7 |
8 | import type { Prisma } from "@zenstackhq/runtime/models";
9 | import { type GetNextArgs, type QueryOptions, type InfiniteQueryOptions, type MutationOptions, type PickEnumerable } from '@zenstackhq/swr/runtime';
10 | import type { PolicyCrudKind } from '@zenstackhq/runtime'
11 | import metadata from './__model_meta';
12 | import * as request from '@zenstackhq/swr/runtime';
13 |
14 | export function useCreateSpace(options?: MutationOptions | undefined, unknown, Prisma.SpaceCreateArgs>) {
15 | const mutation = request.useModelMutation('Space', 'POST', 'create', metadata, options, true);
16 | return {
17 | ...mutation,
18 | trigger: (args: Prisma.SelectSubset) => {
19 | return mutation.trigger(args, options as any) as Promise | undefined>;
20 | }
21 | };
22 | }
23 |
24 | export function useCreateManySpace(options?: MutationOptions) {
25 | const mutation = request.useModelMutation('Space', 'POST', 'createMany', metadata, options, false);
26 | return {
27 | ...mutation,
28 | trigger: (args: Prisma.SelectSubset) => {
29 | return mutation.trigger(args, options as any) as Promise;
30 | }
31 | };
32 | }
33 |
34 | export function useFindManySpace(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>>) {
35 | return request.useModelQuery('Space', 'findMany', args, options);
36 | }
37 |
38 | export function useInfiniteFindManySpace>>(getNextArgs: GetNextArgs | undefined, R>, options?: InfiniteQueryOptions>>) {
39 | return request.useInfiniteModelQuery('Space', 'findMany', getNextArgs, options);
40 | }
41 |
42 | export function useFindUniqueSpace(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
43 | return request.useModelQuery('Space', 'findUnique', args, options);
44 | }
45 |
46 | export function useFindFirstSpace(args?: Prisma.SelectSubset, options?: QueryOptions & { $optimistic?: boolean }>) {
47 | return request.useModelQuery('Space', 'findFirst', args, options);
48 | }
49 |
50 | export function useUpdateSpace(options?: MutationOptions | undefined, unknown, Prisma.SpaceUpdateArgs>) {
51 | const mutation = request.useModelMutation('Space', 'PUT', 'update', metadata, options, true);
52 | return {
53 | ...mutation,
54 | trigger: (args: Prisma.SelectSubset) => {
55 | return mutation.trigger(args, options as any) as Promise | undefined>;
56 | }
57 | };
58 | }
59 |
60 | export function useUpdateManySpace(options?: MutationOptions) {
61 | const mutation = request.useModelMutation('Space', 'PUT', 'updateMany', metadata, options, false);
62 | return {
63 | ...mutation,
64 | trigger: (args: Prisma.SelectSubset) => {
65 | return mutation.trigger(args, options as any) as Promise;
66 | }
67 | };
68 | }
69 |
70 | export function useUpsertSpace(options?: MutationOptions | undefined, unknown, Prisma.SpaceUpsertArgs>) {
71 | const mutation = request.useModelMutation('Space', 'POST', 'upsert', metadata, options, true);
72 | return {
73 | ...mutation,
74 | trigger: (args: Prisma.SelectSubset) => {
75 | return mutation.trigger(args, options as any) as Promise | undefined>;
76 | }
77 | };
78 | }
79 |
80 | export function useDeleteSpace(options?: MutationOptions | undefined, unknown, Prisma.SpaceDeleteArgs>) {
81 | const mutation = request.useModelMutation('Space', 'DELETE', 'delete', metadata, options, true);
82 | return {
83 | ...mutation,
84 | trigger: (args: Prisma.SelectSubset) => {
85 | return mutation.trigger(args, options as any) as Promise | undefined>;
86 | }
87 | };
88 | }
89 |
90 | export function useDeleteManySpace(options?: MutationOptions) {
91 | const mutation = request.useModelMutation('Space', 'DELETE', 'deleteMany', metadata, options, false);
92 | return {
93 | ...mutation,
94 | trigger: (args: Prisma.SelectSubset) => {
95 | return mutation.trigger(args, options as any) as Promise;
96 | }
97 | };
98 | }
99 |
100 | export function useAggregateSpace(args?: Prisma.Subset, options?: QueryOptions>) {
101 | return request.useModelQuery('Space', 'aggregate', args, options);
102 | }
103 |
104 | export function useGroupBySpace>, Prisma.Extends<'take', Prisma.Keys>>, OrderByArg extends Prisma.True extends HasSelectOrTake ? { orderBy: Prisma.SpaceGroupByArgs['orderBy'] } : { orderBy?: Prisma.SpaceGroupByArgs['orderBy'] }, OrderFields extends Prisma.ExcludeUnderscoreKeys>>, ByFields extends Prisma.MaybeTupleToUnion, ByValid extends Prisma.Has, HavingFields extends Prisma.GetHavingFields, HavingValid extends Prisma.Has, ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, InputErrors extends ByEmpty extends Prisma.True
105 | ? `Error: "by" must not be empty.`
106 | : HavingValid extends Prisma.False
107 | ? {
108 | [P in HavingFields]: P extends ByFields
109 | ? never
110 | : P extends string
111 | ? `Error: Field "${P}" used in "having" needs to be provided in "by".`
112 | : [
113 | Error,
114 | 'Field ',
115 | P,
116 | ` in "having" needs to be provided in "by"`,
117 | ]
118 | }[HavingFields]
119 | : 'take' extends Prisma.Keys
120 | ? 'orderBy' extends Prisma.Keys
121 | ? ByValid extends Prisma.True
122 | ? {}
123 | : {
124 | [P in OrderFields]: P extends ByFields
125 | ? never
126 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
127 | }[OrderFields]
128 | : 'Error: If you provide "take", you also need to provide "orderBy"'
129 | : 'skip' extends Prisma.Keys
130 | ? 'orderBy' extends Prisma.Keys
131 | ? ByValid extends Prisma.True
132 | ? {}
133 | : {
134 | [P in OrderFields]: P extends ByFields
135 | ? never
136 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
137 | }[OrderFields]
138 | : 'Error: If you provide "skip", you also need to provide "orderBy"'
139 | : ByValid extends Prisma.True
140 | ? {}
141 | : {
142 | [P in OrderFields]: P extends ByFields
143 | ? never
144 | : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
145 | }[OrderFields]>(args?: Prisma.SubsetIntersection & InputErrors, options?: QueryOptions<{} extends InputErrors ?
146 | Array &
147 | {
148 | [P in ((keyof T) & (keyof Prisma.SpaceGroupByOutputType))]: P extends '_count'
149 | ? T[P] extends boolean
150 | ? number
151 | : Prisma.GetScalarType
152 | : Prisma.GetScalarType
153 | }
154 | > : InputErrors>) {
155 | return request.useModelQuery('Space', 'groupBy', args, options);
156 | }
157 |
158 | export function useCountSpace(args?: Prisma.Subset, options?: QueryOptions : number>) {
159 | return request.useModelQuery('Space', 'count', args, options);
160 | }
161 |
162 | export function useCheckSpace(args: { operation: PolicyCrudKind; where?: { id?: string; ownerId?: string; name?: string; slug?: string }; }, options?: QueryOptions) {
163 | return request.useModelQuery('Space', 'check', args, options);
164 | }
165 |
--------------------------------------------------------------------------------
/lib/hooks/todo.ts:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * This file was generated by ZenStack CLI.
3 | ******************************************************************************/
4 |
5 | /* eslint-disable */
6 | // @ts-nocheck
7 |
8 | import type { Prisma } from "@zenstackhq/runtime/models";
9 | import { type GetNextArgs, type QueryOptions, type InfiniteQueryOptions, type MutationOptions, type PickEnumerable } from '@zenstackhq/swr/runtime';
10 | import type { PolicyCrudKind } from '@zenstackhq/runtime'
11 | import metadata from './__model_meta';
12 | import * as request from '@zenstackhq/swr/runtime';
13 |
14 | export function useCreateTodo(options?: MutationOptions | undefined, unknown, Prisma.TodoCreateArgs>) {
15 | const mutation = request.useModelMutation('Todo', 'POST', 'create', metadata, options, true);
16 | return {
17 | ...mutation,
18 | trigger: (args: Prisma.SelectSubset