Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
009e16162e | ||
|
|
9f2c3d24a2 | ||
|
|
54ca043e27 | ||
|
|
521e6e4eda | ||
|
|
05bebffba1 | ||
|
|
6efd3ba9a0 | ||
|
|
c067292fec | ||
|
|
b64e374d76 | ||
|
|
d18cf4b6be | ||
|
|
e46b8d9421 |
@@ -0,0 +1,7 @@
|
|||||||
|
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
|
||||||
|
.vscode
|
||||||
|
node_modules/
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
@@ -13,14 +13,14 @@ FRONTEND_HOST=http://localhost:5173
|
|||||||
# Environment: local, staging, production
|
# Environment: local, staging, production
|
||||||
ENVIRONMENT=local
|
ENVIRONMENT=local
|
||||||
|
|
||||||
PROJECT_NAME="Full Stack FastAPI Project"
|
PROJECT_NAME="BaiPanSou"
|
||||||
STACK_NAME=full-stack-fastapi-project
|
STACK_NAME=BaiPanSou
|
||||||
|
|
||||||
# Backend
|
# Backend
|
||||||
BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com"
|
BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com"
|
||||||
SECRET_KEY=changethis
|
SECRET_KEY=baomihua200712
|
||||||
FIRST_SUPERUSER=admin@example.com
|
FIRST_SUPERUSER=mzaxd0712@gmail.com
|
||||||
FIRST_SUPERUSER_PASSWORD=changethis
|
FIRST_SUPERUSER_PASSWORD=200712
|
||||||
|
|
||||||
# Emails
|
# Emails
|
||||||
SMTP_HOST=
|
SMTP_HOST=
|
||||||
@@ -32,11 +32,11 @@ SMTP_SSL=False
|
|||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
|
|
||||||
# Postgres
|
# Postgres
|
||||||
POSTGRES_SERVER=localhost
|
POSTGRES_SERVER=192.168.1.99
|
||||||
POSTGRES_PORT=5432
|
POSTGRES_PORT=5432
|
||||||
POSTGRES_DB=app
|
POSTGRES_DB=bps_dev
|
||||||
POSTGRES_USER=postgres
|
POSTGRES_USER=mzaxd
|
||||||
POSTGRES_PASSWORD=changethis
|
POSTGRES_PASSWORD=rootroot
|
||||||
|
|
||||||
SENTRY_DSN=
|
SENTRY_DSN=
|
||||||
|
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ node_modules/
|
|||||||
/playwright-report/
|
/playwright-report/
|
||||||
/blob-report/
|
/blob-report/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
|
backend/.idea/
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
__pycache__
|
||||||
|
app.egg-info
|
||||||
|
*.pyc
|
||||||
|
.mypy_cache
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
|
.cache
|
||||||
|
.venv
|
||||||
@@ -75,7 +75,6 @@ class ItemUpdate(ItemBase):
|
|||||||
# Database model, database table inferred from class name
|
# Database model, database table inferred from class name
|
||||||
class Item(ItemBase, table=True):
|
class Item(ItemBase, table=True):
|
||||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||||
title: str = Field(max_length=255)
|
|
||||||
owner_id: uuid.UUID = Field(
|
owner_id: uuid.UUID = Field(
|
||||||
foreign_key="user.id", nullable=False, ondelete="CASCADE"
|
foreign_key="user.id", nullable=False, ondelete="CASCADE"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
openapi.json
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
/playwright/.auth/
|
||||||
@@ -129,7 +129,6 @@ const EditUser = ({ user }: EditUserProps) => {
|
|||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
required
|
|
||||||
invalid={!!errors.password}
|
invalid={!!errors.password}
|
||||||
errorText={errors.password?.message}
|
errorText={errors.password?.message}
|
||||||
label="Set Password"
|
label="Set Password"
|
||||||
@@ -148,7 +147,6 @@ const EditUser = ({ user }: EditUserProps) => {
|
|||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
required
|
|
||||||
invalid={!!errors.confirm_password}
|
invalid={!!errors.confirm_password}
|
||||||
errorText={errors.confirm_password?.message}
|
errorText={errors.confirm_password?.message}
|
||||||
label="Confirm Password"
|
label="Confirm Password"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const Sidebar = () => {
|
|||||||
<DrawerBody>
|
<DrawerBody>
|
||||||
<Flex flexDir="column" justify="space-between">
|
<Flex flexDir="column" justify="space-between">
|
||||||
<Box>
|
<Box>
|
||||||
<SidebarItems />
|
<SidebarItems onClose={() => setOpen(false)} />
|
||||||
<Flex
|
<Flex
|
||||||
as="button"
|
as="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -19,12 +19,7 @@ const UserMenu = () => {
|
|||||||
<Flex>
|
<Flex>
|
||||||
<MenuRoot>
|
<MenuRoot>
|
||||||
<MenuTrigger asChild p={2}>
|
<MenuTrigger asChild p={2}>
|
||||||
<Button
|
<Button data-testid="user-menu" variant="solid" maxW="sm" truncate>
|
||||||
data-testid="user-menu"
|
|
||||||
variant="solid"
|
|
||||||
maxW="150px"
|
|
||||||
truncate
|
|
||||||
>
|
|
||||||
<FaUserAstronaut fontSize="18" />
|
<FaUserAstronaut fontSize="18" />
|
||||||
<Text>{user?.full_name || "User"}</Text>
|
<Text>{user?.full_name || "User"}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
import { Skeleton, Table } from "@chakra-ui/react"
|
import { Table } from "@chakra-ui/react"
|
||||||
|
import { SkeletonText } from "../ui/skeleton"
|
||||||
|
|
||||||
const PendingItems = () => (
|
const PendingItems = () => (
|
||||||
<Table.Root size={{ base: "sm", md: "md" }}>
|
<Table.Root size={{ base: "sm", md: "md" }}>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeader w="30%">ID</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">ID</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="30%">Title</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Title</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="30%">Description</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Description</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="10%">Actions</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Actions</Table.ColumnHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{[...Array(5)].map((_, index) => (
|
{[...Array(5)].map((_, index) => (
|
||||||
<Table.Row key={index}>
|
<Table.Row key={index}>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,33 +1,34 @@
|
|||||||
import { Skeleton, Table } from "@chakra-ui/react"
|
import { Table } from "@chakra-ui/react"
|
||||||
|
import { SkeletonText } from "../ui/skeleton"
|
||||||
|
|
||||||
const PendingUsers = () => (
|
const PendingUsers = () => (
|
||||||
<Table.Root size={{ base: "sm", md: "md" }}>
|
<Table.Root size={{ base: "sm", md: "md" }}>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeader w="20%">Full name</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Full name</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="25%">Email</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Email</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="15%">Role</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Role</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="20%">Status</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Status</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="20%">Actions</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Actions</Table.ColumnHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{[...Array(5)].map((_, index) => (
|
{[...Array(5)].map((_, index) => (
|
||||||
<Table.Row key={index}>
|
<Table.Row key={index}>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Skeleton h="20px" />
|
<SkeletonText noOfLines={1} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -47,10 +47,7 @@ const ChangePassword = () => {
|
|||||||
<Heading size="sm" py={4}>
|
<Heading size="sm" py={4}>
|
||||||
Change Password
|
Change Password
|
||||||
</Heading>
|
</Heading>
|
||||||
<Box
|
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||||
as="form"
|
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
|
||||||
>
|
|
||||||
<VStack gap={4} w={{ base: "100%", md: "sm" }}>
|
<VStack gap={4} w={{ base: "100%", md: "sm" }}>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
type="current_password"
|
type="current_password"
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
import {
|
||||||
|
MutationCache,
|
||||||
|
QueryCache,
|
||||||
|
QueryClient,
|
||||||
|
QueryClientProvider,
|
||||||
|
} from "@tanstack/react-query"
|
||||||
import { RouterProvider, createRouter } from "@tanstack/react-router"
|
import { RouterProvider, createRouter } from "@tanstack/react-router"
|
||||||
import React, { StrictMode } from "react"
|
import React, { StrictMode } from "react"
|
||||||
import ReactDOM from "react-dom/client"
|
import ReactDOM from "react-dom/client"
|
||||||
|
|||||||
@@ -61,17 +61,17 @@ function UsersTable() {
|
|||||||
<Table.Root size={{ base: "sm", md: "md" }}>
|
<Table.Root size={{ base: "sm", md: "md" }}>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeader w="20%">Full name</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Full name</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="25%">Email</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Email</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="15%">Role</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Role</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="20%">Status</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Status</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="20%">Actions</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Actions</Table.ColumnHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{users?.map((user) => (
|
{users?.map((user) => (
|
||||||
<Table.Row key={user.id} opacity={isPlaceholderData ? 0.5 : 1}>
|
<Table.Row key={user.id} opacity={isPlaceholderData ? 0.5 : 1}>
|
||||||
<Table.Cell w="20%" color={!user.full_name ? "gray" : "inherit"}>
|
<Table.Cell color={!user.full_name ? "gray" : "inherit"}>
|
||||||
{user.full_name || "N/A"}
|
{user.full_name || "N/A"}
|
||||||
{currentUser?.id === user.id && (
|
{currentUser?.id === user.id && (
|
||||||
<Badge ml="1" colorScheme="teal">
|
<Badge ml="1" colorScheme="teal">
|
||||||
@@ -79,14 +79,14 @@ function UsersTable() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell w="25%">{user.email}</Table.Cell>
|
<Table.Cell truncate maxW="sm">
|
||||||
<Table.Cell w="15%">
|
{user.email}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
{user.is_superuser ? "Superuser" : "User"}
|
{user.is_superuser ? "Superuser" : "User"}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell w="20%">
|
<Table.Cell>{user.is_active ? "Active" : "Inactive"}</Table.Cell>
|
||||||
{user.is_active ? "Active" : "Inactive"}
|
<Table.Cell>
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell w="20%">
|
|
||||||
<UserActionsMenu
|
<UserActionsMenu
|
||||||
user={user}
|
user={user}
|
||||||
disabled={currentUser?.id === user.id}
|
disabled={currentUser?.id === user.id}
|
||||||
|
|||||||
@@ -85,19 +85,19 @@ function ItemsTable() {
|
|||||||
<Table.Root size={{ base: "sm", md: "md" }}>
|
<Table.Root size={{ base: "sm", md: "md" }}>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeader w="30%">ID</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">ID</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="30%">Title</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Title</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="30%">Description</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Description</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader w="10%">Actions</Table.ColumnHeader>
|
<Table.ColumnHeader w="sm">Actions</Table.ColumnHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{items?.map((item) => (
|
{items?.map((item) => (
|
||||||
<Table.Row key={item.id} opacity={isPlaceholderData ? 0.5 : 1}>
|
<Table.Row key={item.id} opacity={isPlaceholderData ? 0.5 : 1}>
|
||||||
<Table.Cell truncate maxW="30%">
|
<Table.Cell truncate maxW="sm">
|
||||||
{item.id}
|
{item.id}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell truncate maxW="30%">
|
<Table.Cell truncate maxW="sm">
|
||||||
{item.title}
|
{item.title}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell
|
<Table.Cell
|
||||||
@@ -107,7 +107,7 @@ function ItemsTable() {
|
|||||||
>
|
>
|
||||||
{item.description || "N/A"}
|
{item.description || "N/A"}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell width="10%">
|
<Table.Cell>
|
||||||
<ItemActionsMenu item={item} />
|
<ItemActionsMenu item={item} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
|
|||||||
@@ -2,6 +2,17 @@
|
|||||||
|
|
||||||
## Latest Changes
|
## Latest Changes
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* 🐛 Close sidebar drawer on user selection. PR [#1515](https://github.com/fastapi/full-stack-fastapi-template/pull/1515) by [@dtellz](https://github.com/dtellz).
|
||||||
|
* 🐛 Fix required password validation when editing user fields. PR [#1508](https://github.com/fastapi/full-stack-fastapi-template/pull/1508) by [@jpizquierdo](https://github.com/jpizquierdo).
|
||||||
|
|
||||||
|
### Refactors
|
||||||
|
|
||||||
|
* ♻️ Remove redundant field in inherited class. PR [#1520](https://github.com/fastapi/full-stack-fastapi-template/pull/1520) by [@tzway](https://github.com/tzway).
|
||||||
|
* 🎨 Add minor UI tweaks in Skeletons and other components. PR [#1507](https://github.com/fastapi/full-stack-fastapi-template/pull/1507) by [@alejsdev](https://github.com/alejsdev).
|
||||||
|
* 🎨 Add minor UI tweaks. PR [#1506](https://github.com/fastapi/full-stack-fastapi-template/pull/1506) by [@alejsdev](https://github.com/alejsdev).
|
||||||
|
|
||||||
## 0.8.0
|
## 0.8.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
Reference in New Issue
Block a user