Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,22 @@ DOCKER_DATABASE_NAME="startui"
DOCKER_DATABASE_USERNAME="startui"
DOCKER_DATABASE_PASSWORD="startui"

DOCKER_MINIO_API_PORT="9000"
DOCKER_MINIO_UI_PORT="9001"
DOCKER_MINIO_USERNAME="minioadmin"
DOCKER_MINIO_PASSWORD="minioadmin"

# S3
S3_ENDPOINT="http://127.0.0.1:${DOCKER_MINIO_API_PORT}"
S3_BUCKET_NAME="start-ui-bucket"
S3_ACCESS_KEY_ID="startui-access-key"
S3_SECRET_ACCESS_KEY="startui-secret-key"
S3_REGION="default"

# PUBLIC CONFIG
VITE_S3_BUCKET_PUBLIC_URL="http://127.0.0.1:${DOCKER_MINIO_API_PORT}/${S3_BUCKET_NAME}"
VITE_BASE_URL="http://localhost:${VITE_PORT}"

# Use the following environment variables to show the environment name.
VITE_ENV_NAME="LOCAL"
VITE_ENV_EMOJI="🚧"
Expand Down
38 changes: 38 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,41 @@ services:
interval: 10s
timeout: 5s
retries: 5

minio:
image: minio/minio:RELEASE.2025-07-23T15-54-02Z-cpuv1
ports:
- '${DOCKER_MINIO_API_PORT}:9000'
- '${DOCKER_MINIO_UI_PORT}:9001'
environment:
- MINIO_ROOT_USER=${DOCKER_MINIO_USERNAME}
- MINIO_ROOT_PASSWORD=${DOCKER_MINIO_PASSWORD}
volumes:
- minio:/data/minio
command: minio server /data/minio --console-address :${DOCKER_MINIO_UI_PORT}
healthcheck:
test: ['CMD', 'mc', 'ready', 'local']
interval: 5s
timeout: 5s
retries: 5

createbucket:
image: minio/mc:RELEASE.2025-08-13T08-35-41Z-cpuv1
profiles:
- init
depends_on:
minio:
condition: service_healthy
entrypoint: ['']
command: ["sh", "-c", "
mc alias set default http://minio:${DOCKER_MINIO_API_PORT} \"${DOCKER_MINIO_USERNAME}\" \"${DOCKER_MINIO_PASSWORD}\";
mc admin user add default ${S3_ACCESS_KEY_ID} ${S3_SECRET_ACCESS_KEY};
mc admin policy attach default readwrite --user ${S3_ACCESS_KEY_ID};
mc mb --ignore-existing default/${S3_BUCKET_NAME} 2>/dev/null;
mc anonymous set download default/${S3_BUCKET_NAME};
echo 'Bucket configuration completed successfully';
"]
restart: "no"

volumes:
minio:
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"test:ui": "vitest",
"e2e": "dotenv -- cross-env playwright test",
"e2e:ui": "dotenv -- cross-env playwright test --ui",
"dk:init": "docker compose up -d",
"dk:init": "docker compose --profile init up -d",
"dk:start": "docker compose start",
"dk:stop": "docker compose stop",
"dk:clear": "docker compose down --volumes",
Expand All @@ -42,6 +42,7 @@
"db:seed": "dotenv -- cross-env node ./run-jiti ./prisma/seed/index.ts"
},
"dependencies": {
"@aws-sdk/client-s3": "3.864.0",
"@base-ui-components/react": "1.0.0-beta.1",
"@bearstudio/ui-state": "1.0.2",
"@better-auth/expo": "1.3.27",
Expand All @@ -67,6 +68,7 @@
"@tanstack/zod-adapter": "1.132.47",
"@uidotdev/usehooks": "2.4.1",
"better-auth": "1.3.27",
"better-upload": "2.0.3",
"boring-avatars": "2.0.4",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
Expand Down
4,615 changes: 2,802 additions & 1,813 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ model User {
name String
email String
emailVerified Boolean
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions Session[]
Expand Down Expand Up @@ -94,6 +93,7 @@ model Book {
genre Genre? @relation(fields: [genreId], references: [id])
genreId String
publisher String?
coverId String?

@@unique([title, author])
@@map("book")
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/field-text/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type FieldTextProps<
TFieldValues,
TName,
{
type: 'text' | 'email' | 'tel';
type: 'text' | 'email' | 'tel' | 'file';
containerProps?: ComponentProps<'div'>;
} & ComponentProps<typeof Input>
Comment on lines +19 to 21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

File input is incorrectly controlled; pass files to RHF and omit value

For type="file", spreading ...field injects a value prop and forwards the raw event, which triggers React warnings and prevents RHF from receiving the File/File[]. Handle the file case specially.

Apply:

@@
-      defaultValue={defaultValue}
+      defaultValue={type === 'file' ? undefined : defaultValue}
@@
-          <Input
-            type={type}
-            id={ctx.id}
-            aria-invalid={fieldState.error ? true : undefined}
-            aria-describedby={
-              !fieldState.error
-                ? `${ctx.descriptionId}`
-                : `${ctx.descriptionId} ${ctx.errorId}`
-            }
-            {...rest}
-            {...field}
-            onChange={(e) => {
-              field.onChange(e);
-              rest.onChange?.(e);
-            }}
-            onBlur={(e) => {
-              field.onBlur();
-              rest.onBlur?.(e);
-            }}
-          />
+          <Input
+            type={type}
+            id={ctx.id}
+            aria-invalid={fieldState.error ? true : undefined}
+            aria-describedby={
+              !fieldState.error
+                ? `${ctx.descriptionId}`
+                : `${ctx.descriptionId} ${ctx.errorId}`
+            }
+            {...rest}
+            // Avoid controlling file inputs; only pass minimal RHF bindings
+            name={field.name}
+            ref={field.ref}
+            {...(type === 'file'
+              ? {
+                  onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
+                    const files = Array.from(e.target.files ?? []);
+                    field.onChange(rest.multiple ? files : files[0] ?? null);
+                    rest.onChange?.(e);
+                  },
+                  onBlur: (e: React.FocusEvent<HTMLInputElement>) => {
+                    field.onBlur();
+                    rest.onBlur?.(e);
+                  },
+                }
+              : {
+                  value: field.value,
+                  onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
+                    field.onChange(e);
+                    rest.onChange?.(e);
+                  },
+                  onBlur: (e: React.FocusEvent<HTMLInputElement>) => {
+                    field.onBlur();
+                    rest.onBlur?.(e);
+                  },
+                })}
+          />

Also applies to: 57-76

🤖 Prompt for AI Agents
In app/components/form/field-text/index.tsx around lines 19-21 (and also apply
the same fix to lines 57-76), the component currently spreads ...field into the
Input which for type="file" passes a value prop and the raw event, causing React
warnings and preventing React Hook Form from receiving File/File[]; modify the
component to handle type==="file" specially by omitting the field.value when
spreading, and wire onChange to extract files (e.g., e.target.files) and call
field.onChange with the File or File[] and call field.onBlur as appropriate;
ensure you also do not pass a value prop to the native file input and keep other
props (name, ref) intact so RHF correctly registers the files.

>;
Expand Down
3 changes: 3 additions & 0 deletions src/components/form/form-field-controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ export const FormFieldController = <

case 'checkbox-group':
return <FieldCheckboxGroup {...props} />;

case 'file':
return <FieldText {...props} />;
// -- ADD NEW FIELD COMPONENT HERE --
}
};
Expand Down
1 change: 1 addition & 0 deletions src/env/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const envClient = createEnv({
.string()
.optional()
.transform((value) => value ?? (isDev ? 'gold' : 'plum')),
VITE_S3_BUCKET_PUBLIC_URL: z.string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Build is red: make URL typed and ensure it’s provided in CI

VITE_S3_BUCKET_PUBLIC_URL is required but undefined in the pipeline. Also validate it as a URL.

Apply:

-    VITE_S3_BUCKET_PUBLIC_URL: z.string(),
+    VITE_S3_BUCKET_PUBLIC_URL: z.string().url(),

Follow‑ups:

  • Add this var to .env.example, local envs, and CI (or set SKIP_ENV_VALIDATION only where acceptable).
  • It must be an absolute base URL (e.g., https://cdn.example.com/bucket/).

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Usages:"
rg -n --heading 'VITE_S3_BUCKET_PUBLIC_URL' app

echo -e "\nCheck .env example(s):"
fd -H '^\.env(\..+)?$' | while read -r f; do
  echo "==> $f"
  rg -n 'VITE_S3_BUCKET_PUBLIC_URL' "$f" || true
done

Length of output: 239


Require & validate VITE_S3_BUCKET_PUBLIC_URL and add it to envs/CI

  • Change validation in app/env/client.ts:
-    VITE_S3_BUCKET_PUBLIC_URL: z.string(),
+    VITE_S3_BUCKET_PUBLIC_URL: z.string().url(),
  • Add VITE_S3_BUCKET_PUBLIC_URL to .env.example and the pipeline/CI secrets with an absolute base URL.
  • Usage note: app/features/account/user-card.tsx:21 builds ${envClient.VITE_S3_BUCKET_PUBLIC_URL}/${session.data?.user.avatarFileId} — normalize the base (no trailing slash) or update the join logic to avoid double slashes.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VITE_S3_BUCKET_PUBLIC_URL: z.string(),
VITE_S3_BUCKET_PUBLIC_URL: z.string().url(),
🤖 Prompt for AI Agents
In app/env/client.ts around line 46 add validation for VITE_S3_BUCKET_PUBLIC_URL
(z.string()) so the client env includes the S3 public base URL; then update
.env.example and your CI/pipeline secrets to provide an absolute base URL;
finally ensure the value is normalized (strip any trailing slash) or update the
usage at app/features/account/user-card.tsx (where it builds
`${envClient.VITE_S3_BUCKET_PUBLIC_URL}/${session.data?.user.avatarFileId}`) to
safely join parts and avoid double slashes.

},
runtimeEnv: {
...envMetaOrProcess,
Expand Down
10 changes: 10 additions & 0 deletions src/env/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ export const envServer = createEnv({
.enum(['true', 'false'])
.prefault(isProd ? 'false' : 'true')
.transform((value) => value === 'true'),

DOCKER_MINIO_API_PORT: z.string().default('9000'),
DOCKER_MINIO_UI_PORT: z.string().default('9001'),
DOCKER_MINIO_USERNAME: z.string(),
DOCKER_MINIO_PASSWORD: z.string(),
S3_ACCESS_KEY_ID: z.string(),
S3_SECRET_ACCESS_KEY: z.string(),
S3_BUCKET_NAME: z.string().default('default'),
S3_REGION: z.string().default('default'),
S3_ENDPOINT: z.string(),
},
runtimeEnv: process.env,
emptyStringAsUndefined: true,
Expand Down
19 changes: 14 additions & 5 deletions src/features/book/book-cover.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import { useTranslation } from 'react-i18next';
import { match, P } from 'ts-pattern';

import { cn } from '@/lib/tailwind/utils';

import { envClient } from '@/env/client';
import { Book } from '@/features/book/schema';

export const BookCover = (props: {
book: Partial<Pick<Book, 'title' | 'author' | 'genre'>>;
book: Partial<Pick<Book, 'title' | 'author' | 'genre' | 'coverId'>>;
variant?: 'default' | 'tiny';
className?: string;
}) => {
const { t } = useTranslation(['book']);

const background = match(props.book.coverId)
.with(
P.string,
(coverId) => `url(${envClient.VITE_S3_BUCKET_PUBLIC_URL}/${coverId})`
)
.with(P._, () => props.book.genre?.color ?? '#333')
.exhaustive();

return (
<div
className={cn(
'@container relative flex aspect-[2/3] flex-col justify-between overflow-hidden rounded-sm bg-neutral-800 p-[10%] pl-[16%] text-white shadow-2xl',
'@container relative flex aspect-[2/3] flex-col justify-between overflow-hidden rounded-sm bg-neutral-800 bg-cover bg-center p-[10%] pl-[16%] text-white shadow-2xl',
props.variant === 'tiny' && 'w-8 rounded-xs',
props.className
)}
style={{
background: props.book.genre?.color ?? '#333',
}}
style={{ background }}
>
<div className="absolute inset-y-0 left-0 w-[5%] bg-gradient-to-r from-black/0 to-black/10 bg-blend-screen" />
<div className="absolute inset-y-0 left-[5%] w-[2%] bg-gradient-to-r from-white/0 to-white/20 bg-blend-screen" />
Expand Down
47 changes: 40 additions & 7 deletions src/features/book/manager/form-book-cover.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { useUploadFile } from 'better-upload/client';
import { useFormContext, useWatch } from 'react-hook-form';

import { orpc } from '@/lib/orpc/client';
Expand All @@ -21,14 +22,46 @@
name: 'author',
control: form.control,
});
const coverId = useWatch({
name: 'coverId',
control: form.control,
});

const { upload, uploadedFile } = useUploadFile({
route: 'bookCover',
onUploadComplete: ({ file }) => {
form.setValue('coverId', file.objectKey);
},
});

// [TODO] Handle upload errors

Check warning on line 37 in src/features/book/manager/form-book-cover.tsx

View workflow job for this annotation

GitHub Actions / 🧹 Linter

Complete the task associated to this "TODO" comment

const genre = genresQuery.data?.items.find((item) => item.id === genreId);
return (
<BookCover
book={{
title,
author,
genre,
}}
/>
<div className="relative">
<label htmlFor="coverId">
<input
className="hidden"
id="coverId"
type="file"
name="coverId"
onChange={(e) => {
if (e.target.files?.[0]) {
upload(e.target.files[0]);
}
}}
/>
<input type="hidden" {...form.register('coverId')} />
<BookCover
Comment on lines +30 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle upload failures instead of leaving a TODO

The TODO at Line 37 is tripping the linter and, more importantly, we still surface no feedback if the upload promise rejects. Please remove the TODO by actually handling errors—catch the rejection and surface a validation message (ideally localized) so the form can react.

-      onUploadComplete: ({ file }) => {
-        form.setValue('coverId', file.objectKey);
-      },
+      onUploadComplete: ({ file }) => {
+        form.clearErrors('coverId');
+        form.setValue('coverId', file.objectKey, { shouldDirty: true });
+      },
     });
 
-  // [TODO] Handle upload errors
+  const handleUpload = (file: File) => {
+    void upload(file).catch(() => {
+      form.setError('coverId', {
+        type: 'manual',
+        message: 'cover_upload_failed',
+      });
+    });
+  };
 ...
-              upload(e.target.files[0]);
+              handleUpload(e.target.files[0]);

Replace 'cover_upload_failed' with your localized copy or translation lookup.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { upload, uploadedFile } = useUploadFile({
route: 'bookCover',
onUploadComplete: ({ file }) => {
form.setValue('coverId', file.objectKey);
},
});
// [TODO] Handle upload errors
const genre = genresQuery.data?.items.find((item) => item.id === genreId);
return (
<BookCover
book={{
title,
author,
genre,
}}
/>
<div className="relative">
<label htmlFor="coverId">
<input
className="hidden"
id="coverId"
type="file"
name="coverId"
onChange={(e) => {
if (e.target.files?.[0]) {
upload(e.target.files[0]);
}
}}
/>
<input type="hidden" {...form.register('coverId')} />
<BookCover
const { upload, uploadedFile } = useUploadFile({
route: 'bookCover',
onUploadComplete: ({ file }) => {
form.clearErrors('coverId');
form.setValue('coverId', file.objectKey, { shouldDirty: true });
},
});
const handleUpload = (file: File) => {
void upload(file).catch(() => {
form.setError('coverId', {
type: 'manual',
message: 'cover_upload_failed',
});
});
};
const genre = genresQuery.data?.items.find((item) => item.id === genreId);
return (
<div className="relative">
<label htmlFor="coverId">
<input
className="hidden"
id="coverId"
type="file"
name="coverId"
onChange={(e) => {
if (e.target.files?.[0]) {
handleUpload(e.target.files[0]);
}
}}
/>
<input type="hidden" {...form.register('coverId')} />
<BookCover
🧰 Tools
🪛 GitHub Actions: 🔎 Code Quality

[error] 37-37: ESLint: Complete the task associated to this "TODO" comment. (sonarjs/todo-tag)

🪛 GitHub Check: 🧹 Linter

[warning] 37-37:
Complete the task associated to this "TODO" comment

🤖 Prompt for AI Agents
In src/features/book/manager/form-book-cover.tsx around lines 30 to 55, replace
the TODO by catching upload failures and surfacing a form validation error: wrap
the upload call in a try/catch (or handle the returned promise) and on failure
call form.setError('coverId', { type: 'manual', message:
t('cover_upload_failed') }) (or your i18n lookup) so the UI shows a localized
error; also ensure any previous error is cleared on successful upload with
form.clearErrors('coverId') and remove the TODO comment.

className="hover:cursor-pointer"
book={{
title,
author,
genre,
coverId: uploadedFile?.objectKey ?? coverId,
}}
/>
</label>
</div>
);
};
1 change: 1 addition & 0 deletions src/features/book/manager/page-book-update.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const PageBookUpdate = (props: { params: { id: string } }) => {
author: bookQuery.data?.author ?? '',
genreId: bookQuery.data?.genre?.id ?? null!,
publisher: bookQuery.data?.publisher ?? '',
coverId: bookQuery.data?.coverId ?? '',
},
});

Expand Down
3 changes: 2 additions & 1 deletion src/features/book/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ export const zBook = () =>
publisher: zu.fieldText.nullish(),
createdAt: z.date(),
updatedAt: z.date(),
coverId: z.string().nullish(),
});

export type FormFieldsBook = z.infer<ReturnType<typeof zFormFieldsBook>>;
export const zFormFieldsBook = () =>
zBook()
.pick({ title: true, author: true, publisher: true })
.pick({ title: true, author: true, publisher: true, coverId: true })
.extend({ genreId: zu.fieldText.required() });
1 change: 0 additions & 1 deletion src/features/user/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export const zUser = () =>
),
emailVerified: z.boolean(),
role: zRole().nullish(),
image: z.string().nullish(),
createdAt: z.date(),
updatedAt: z.date(),
onboardedAt: z.date().nullish(),
Expand Down
12 changes: 12 additions & 0 deletions src/lib/s3/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { S3Client } from '@aws-sdk/client-s3';

import { envServer } from '@/env/server';

export const s3client = new S3Client({
endpoint: envServer.S3_ENDPOINT,
credentials: {
accessKeyId: envServer.S3_ACCESS_KEY_ID,
secretAccessKey: envServer.S3_SECRET_ACCESS_KEY,
},
region: envServer.S3_REGION,
});
Comment on lines +5 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Enable path-style requests for MinIO

When targeting MinIO (or any custom endpoint with a non-S3 domain/port), the SDK must use path-style URLs; otherwise it will try bucket.endpoint hostnames like default.localhost:9000, which fail to resolve. Add forcePathStyle: true to keep uploads working.

 export const s3client = new S3Client({
   endpoint: envServer.S3_ENDPOINT,
+  forcePathStyle: true,
   credentials: {
     accessKeyId: envServer.S3_ACCESS_KEY_ID,
     secretAccessKey: envServer.S3_SECRET_ACCESS_KEY,
   },
   region: envServer.S3_REGION,
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const s3client = new S3Client({
endpoint: envServer.S3_ENDPOINT,
credentials: {
accessKeyId: envServer.S3_ACCESS_KEY_ID,
secretAccessKey: envServer.S3_SECRET_ACCESS_KEY,
},
region: envServer.S3_REGION,
});
export const s3client = new S3Client({
endpoint: envServer.S3_ENDPOINT,
forcePathStyle: true,
credentials: {
accessKeyId: envServer.S3_ACCESS_KEY_ID,
secretAccessKey: envServer.S3_SECRET_ACCESS_KEY,
},
region: envServer.S3_REGION,
});
🤖 Prompt for AI Agents
In src/lib/s3/index.ts around lines 5 to 12, the S3Client configuration lacks
path-style requests, which breaks usage against MinIO/custom endpoints; update
the S3Client constructor options to include forcePathStyle: true so the SDK uses
path-style URLs (bucket in path) instead of virtual-hosted style (bucket as
hostname), ensuring uploads work with MinIO and non-S3 endpoints.

21 changes: 21 additions & 0 deletions src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Route as IndexRouteImport } from './routes/index'
import { Route as ManagerIndexRouteImport } from './routes/manager/index'
import { Route as LoginIndexRouteImport } from './routes/login/index'
import { Route as AppIndexRouteImport } from './routes/app/index'
import { Route as ApiUploadRouteImport } from './routes/api/upload'
import { Route as ManagerUsersIndexRouteImport } from './routes/manager/users/index'
import { Route as ManagerDashboardIndexRouteImport } from './routes/manager/dashboard.index'
import { Route as ManagerBooksIndexRouteImport } from './routes/manager/books/index'
Expand Down Expand Up @@ -75,6 +76,11 @@ const AppIndexRoute = AppIndexRouteImport.update({
path: '/',
getParentRoute: () => AppRouteRoute,
} as any)
const ApiUploadRoute = ApiUploadRouteImport.update({
id: '/api/upload',
path: '/api/upload',
getParentRoute: () => rootRouteImport,
} as any)
const ManagerUsersIndexRoute = ManagerUsersIndexRouteImport.update({
id: '/users/',
path: '/users/',
Expand Down Expand Up @@ -198,6 +204,7 @@ export interface FileRoutesByFullPath {
'/app': typeof AppRouteRouteWithChildren
'/login': typeof LoginRouteRouteWithChildren
'/manager': typeof ManagerRouteRouteWithChildren
'/api/upload': typeof ApiUploadRoute
'/app/': typeof AppIndexRoute
'/login/': typeof LoginIndexRoute
'/manager/': typeof ManagerIndexRoute
Expand Down Expand Up @@ -227,6 +234,7 @@ export interface FileRoutesByFullPath {
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/api/upload': typeof ApiUploadRoute
'/app': typeof AppIndexRoute
'/login': typeof LoginIndexRoute
'/manager': typeof ManagerIndexRoute
Expand Down Expand Up @@ -260,6 +268,7 @@ export interface FileRoutesById {
'/app': typeof AppRouteRouteWithChildren
'/login': typeof LoginRouteRouteWithChildren
'/manager': typeof ManagerRouteRouteWithChildren
'/api/upload': typeof ApiUploadRoute
'/app/': typeof AppIndexRoute
'/login/': typeof LoginIndexRoute
'/manager/': typeof ManagerIndexRoute
Expand Down Expand Up @@ -294,6 +303,7 @@ export interface FileRouteTypes {
| '/app'
| '/login'
| '/manager'
| '/api/upload'
| '/app/'
| '/login/'
| '/manager/'
Expand Down Expand Up @@ -323,6 +333,7 @@ export interface FileRouteTypes {
fileRoutesByTo: FileRoutesByTo
to:
| '/'
| '/api/upload'
| '/app'
| '/login'
| '/manager'
Expand Down Expand Up @@ -355,6 +366,7 @@ export interface FileRouteTypes {
| '/app'
| '/login'
| '/manager'
| '/api/upload'
| '/app/'
| '/login/'
| '/manager/'
Expand Down Expand Up @@ -388,6 +400,7 @@ export interface RootRouteChildren {
AppRouteRoute: typeof AppRouteRouteWithChildren
LoginRouteRoute: typeof LoginRouteRouteWithChildren
ManagerRouteRoute: typeof ManagerRouteRouteWithChildren
ApiUploadRoute: typeof ApiUploadRoute
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
ApiOpenapiAppRoute: typeof ApiOpenapiAppRouteWithChildren
ApiOpenapiAuthRoute: typeof ApiOpenapiAuthRouteWithChildren
Expand Down Expand Up @@ -447,6 +460,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AppIndexRouteImport
parentRoute: typeof AppRouteRoute
}
'/api/upload': {
id: '/api/upload'
path: '/api/upload'
fullPath: '/api/upload'
preLoaderRoute: typeof ApiUploadRouteImport
parentRoute: typeof rootRouteImport
}
'/manager/users/': {
id: '/manager/users/'
path: '/users'
Expand Down Expand Up @@ -706,6 +726,7 @@ const rootRouteChildren: RootRouteChildren = {
AppRouteRoute: AppRouteRouteWithChildren,
LoginRouteRoute: LoginRouteRouteWithChildren,
ManagerRouteRoute: ManagerRouteRouteWithChildren,
ApiUploadRoute: ApiUploadRoute,
ApiAuthSplatRoute: ApiAuthSplatRoute,
ApiOpenapiAppRoute: ApiOpenapiAppRouteWithChildren,
ApiOpenapiAuthRoute: ApiOpenapiAuthRouteWithChildren,
Expand Down
Loading
Loading