Compare commits

...

87 Commits

Author SHA1 Message Date
83bd86cc9b ``update Add .env.example file with PocketBase URL configuration for development environment `` 2025-05-16 17:47:33 +08:00
49189a532e ```
update Add development environment configuration, I18n support, route adjustments, and various hooks refactoring
```
2025-05-16 17:47:05 +08:00
6b917c9fb9 ``update Add error constant for empty PocketBase URL and validate it during app initialization `` 2025-05-16 15:56:56 +08:00
aa834a43c9 ``update Add platform-specific back button for Android, replace header layout, use LoadingSpinner, and refactor imports in Lesson page component`` 2025-05-16 15:56:46 +08:00
1775d8c5a3 ``update Remove unused components, imports, and refactor file URL generation in WordPage component`` 2025-05-16 15:56:24 +08:00
779062e247 ``update Replace getStudentAvatar with getStudentAvatarUrl for avatar URL generation in student profile`` 2025-05-16 15:56:19 +08:00
60df47fb8d ``update Extract PocketBase URL to constant and refactor related functions to use it for dynamic file URL generation`` 2025-05-16 15:55:47 +08:00
c87357ff24 ``update Remove unused import path suffix and comment out unused useRequireAuth hook`` 2025-05-16 15:55:30 +08:00
34a7ff7ac9 ``refactor Rename LoadingScreen component to LoadingSpinner and refactor structure for better reusability`` 2025-05-16 15:55:07 +08:00
3556e77a7c ```
add Implement custom sign-out functionality with loading state and error handling
```
2025-05-16 13:26:17 +08:00
57e25ef65f update fix eslint, 2025-05-16 12:51:32 +08:00
af5040ac6c update to use, 2025-05-16 12:07:58 +08:00
7264d643d0 remove unused code, 2025-05-16 11:51:11 +08:00
de47ceea0b ``update Add test section and GIF image to REQ0016 and REQ0020 documentation files`` 2025-05-16 11:16:29 +08:00
7295b46c06 ```
init Add default VS Code workspace configuration with current and test folders
```
2025-05-16 11:16:21 +08:00
471175d08d ``refactor Update tsconfig.json to include trailing comments in compilerOptions paths and exclude patterns `` 2025-05-16 11:16:10 +08:00
dae97f52ae ``refactor Update PocketBase URL in development environment to use local network IP address `` 2025-05-16 11:14:45 +08:00
6e16b2e87d ```
refactor Add sign-in form component with OAuth integration, Zod validation, i18n support, and password visibility toggle following REQ0016 requirements
```
2025-05-16 11:14:38 +08:00
2371e40ad2 init 003_test, 2025-05-16 11:13:50 +08:00
3e3e23aa68 ``refactor Update docker-compose configurations, adjust PocketBase Dockerfile, modify seed scripts to use 'state' instead of 'status', and fix paths in development scripts `` 2025-05-16 11:13:14 +08:00
a8b0a63648 ```refactor Add reset password form component with Zod validation, email input handling, and internationalization support for custom auth flow
```
2025-05-16 11:07:13 +08:00
bf059147c7 ``refactor Implement custom sign-up form component with Zod validation, OAuth integration, and i18n support following REQ0016 requirements`` 2025-05-16 11:03:58 +08:00
4b750b8146 ``refactor Add explicit type annotation to isDevelopment variable in check-is-development utility function`` 2025-05-16 11:03:05 +08:00
11b1d21472 ``refactor Update CMS site name from 'Devias Kit Pro' to 'demo cms' in configuration`` 2025-05-16 11:02:54 +08:00
3d38af8100 ``refactor Enhance error logging in UserProvider by including full error details in development environment for debugging purposes`` 2025-05-16 11:02:45 +08:00
c5eb7100ea ``refactor Standardize status/state naming across students, teachers, and user metas pages, update filters and tabs to use 'state' parameter consistently `` 2025-05-16 11:02:27 +08:00
louiscklaw
f44539bf63 ``refactor Update .gitignore to exclude files with 'old' suffix in any directory`` 2025-05-15 11:35:47 +08:00
louiscklaw
407f622f24 ``refactor Prettier config to add comment placeholder for potential future plugin additions `` 2025-05-15 11:35:40 +08:00
louiscklaw
7e2844dd74 ```
refactor Student and Teacher create/edit forms to implement i18n support, update UI components, and standardize API calls
```
2025-05-15 11:35:29 +08:00
louiscklaw
097918340c ```
refactor Teachers list page to remove outdated implementation, simplifying component structure and improving maintainability
```
2025-05-15 11:31:03 +08:00
louiscklaw
3620837a6a ``refactor UserMeta creation and authentication client to improve documentation and type consistency`` 2025-05-15 11:27:02 +08:00
louiscklaw
c83e8c1b6e ```
refactor Student and UserMeta type definitions to deprecate obsolete fields, standardize structure, and update billing address format
```
2025-05-15 11:13:15 +08:00
louiscklaw
af15a6bce0 ```
refactor Student type definitions to deprecate obsolete fields, standardize structure, and update billing address format
```
2025-05-15 11:12:56 +08:00
louiscklaw
d0215cf23f ```
refactor GetById APIs for Students, Teachers, and UserMetas to use consistent type definitions and expand parameters
```
2025-05-15 11:12:29 +08:00
louiscklaw
e523f80123 ```
refactor UserMeta type definitions to deprecate obsolete fields and standardize structure
```
2025-05-15 11:10:40 +08:00
louiscklaw
7f8f8824a7 ```
refactor getImageUrlFromFile to use Pocketbase records and correct path in teachers routes
```
2025-05-15 11:10:12 +08:00
louiscklaw
2aa96eec62 ```
refactor student and teacher APIs to use COL_USER_METAS collection, standardize role assignment, and update type definitions
```
2025-05-15 09:27:38 +08:00
louiscklaw
ecab41abbf `` fix GetUserById API to include requestKey option for Pocketbase compatibility `` 2025-05-15 09:27:20 +08:00
louiscklaw
5a1832ca89 ```
remove obsolete GetAllCount API and related guidelines from Users.old module
```
2025-05-15 09:27:08 +08:00
louiscklaw
160c93b83d `` refactor GetUserById function and add Create/UpdateUser APIs with type definitions `` 2025-05-15 09:26:36 +08:00
louiscklaw
d4fcc1dd8f ```
add UserActivationEditForm component for user activation management
```
2025-05-15 09:24:41 +08:00
louiscklaw
8e3d463f78 ```
use dynamic route parameter for user ID in UserActivationEditForm
```
2025-05-15 09:24:27 +08:00
louiscklaw
fbf79b040f `` remove placeholder _PROMPT.md file and reference to external draft for UserMeta editing page `` 2025-05-15 09:24:21 +08:00
louiscklaw
e34782844e ```
add user profile navigation logic and update button in SettingContainer
```
2025-05-15 09:19:59 +08:00
louiscklaw
ba8e9cca69 fix translation, 2025-05-15 08:35:33 +08:00
louiscklaw
cc9fe057c1 ```
add abbreviations section and clarify guideline reading requirement
```
2025-05-14 18:45:23 +08:00
louiscklaw
cdd95faa89 ```
add new student menu route and component, update login default values and redirect logic
```
2025-05-14 18:31:04 +08:00
louiscklaw
030fc1a808 update login requirement for mobile, 2025-05-14 18:13:15 +08:00
louiscklaw
05c69481b5 update check session working, 2025-05-14 18:10:22 +08:00
louiscklaw
0fcc194860 in the middle, working for login and logout test, 2025-05-14 17:19:48 +08:00
louiscklaw
56f0f30ffb ```
replace inline loading text with LoadingScreen component in multiple pages
```
2025-05-14 16:27:30 +08:00
louiscklaw
0aefbfaeae fix typo, 2025-05-14 16:25:57 +08:00
louiscklaw
628c72190b fix typo, 2025-05-14 16:18:39 +08:00
louiscklaw
886a314df7 update settings pages in the middle, 2025-05-14 16:18:04 +08:00
louiscklaw
efc2d31f7c ```
add new student info route and related components, update auth guard implementation and signup success redirect
```
2025-05-14 15:40:59 +08:00
louiscklaw
1938e95948 ```
use async/await for authClient.signInWithPassword to ensure proper execution order
```
2025-05-14 15:20:02 +08:00
louiscklaw
8d37fba393 update login flow, in the middle, 2025-05-14 15:17:04 +08:00
louiscklaw
af160edd42 ```
update .gitignore to add api_ts and dist directories to exclusion list
```
2025-05-14 15:16:21 +08:00
louiscklaw
d880420a28 ``add configuration files for CrossNote, VSCode extensions, and documentation for system architecture and design requirements`` 2025-05-14 15:15:45 +08:00
louiscklaw
5bebc1f40e ```
add COL_BILLING_ADDRESS constant and update exports
```
2025-05-13 13:28:21 +08:00
louiscklaw
f4e5f94e17 ``update .gitignore to modify exclusion pattern for _del files from '**/_del' to '**/*del'`` 2025-05-13 13:28:13 +08:00
louiscklaw
2d022cb613 ```
remove Docker Compose configuration files for CMS, Doc, Ionic Mobile, API_TS, and PocketBase services
```
2025-05-13 13:27:56 +08:00
louiscklaw
3560ea79fc "``update teacher and student seed scripts to dynamically loop through row_array and um_row_array instead of fixed iterations``" 2025-05-13 13:27:48 +08:00
louiscklaw
a441e3e52d ```
refactor teacher and user meta management UI by updating form components, replacing COL_TEACHERS with COL_USER_METAS where applicable, adding development environment checks, improving type definitions for user meta including billing address, and fixing parameter naming inconsistencies in form handlers
```
2025-05-13 13:27:41 +08:00
louiscklaw
09ded06dd2 ``update student database operations to use COL_USER_METAS instead of COL_STUDENTS, refactor getStudentById to include expanded billing address data, and add/update related types and functions for student and user meta management`` 2025-05-13 13:27:27 +08:00
louiscklaw
7ecacd0692 ```
refactor student management UI by updating create form component name and adding new edit page with translation support and form integration
```
2025-05-13 13:27:17 +08:00
louiscklaw
8a094afdd2 "``refactor student create and edit forms with translation support, update schema validation, and use new database operations for student management``" 2025-05-13 13:26:58 +08:00
louiscklaw
64ca29cf60 ``add new database operations for billing address module including create, delete, get, update functions and related type definitions`` 2025-05-13 13:26:41 +08:00
louiscklaw
1aa0502edc ``fix inconsistent quotes in code and update schema with additional fields and rules`` 2025-05-13 13:26:22 +08:00
louiscklaw
3e1f2e1057 ```
add billing address seed data and update user seed scripts with teacher and student roles
```
2025-05-13 12:35:05 +08:00
louiscklaw
9be33f641f ```
add 'visible', 'state', 'company', 'taxId', 'timezone', 'language', 'currency' fields and update 'billingAddress' and 't1' collection rules in PocketBase seed schema
```
2025-05-13 12:34:51 +08:00
louiscklaw
30e4c69343 ```
add Docker Compose configuration files for CMS, Doc, Ionic Mobile, API_TS, and PocketBase services, along with .env file for environment variables and related scripts
```
2025-05-13 06:58:49 +08:00
louiscklaw
04634b5c65 ``update backup script to change archive directory name and add script to recursively remove node_modules directories`` 2025-05-12 19:25:19 +08:00
louiscklaw
f659020d89 ``add TODO list file for Ionic mobile project`` 2025-05-12 19:24:45 +08:00
louiscklaw
184aaa1b0a ``fix HTML doctype declaration in index.html for Ionic mobile project`` 2025-05-12 19:24:39 +08:00
louiscklaw
a6170778cd ```
add new hooks for fetching QuizCRQuestions and categories, update related components to use the new hooks, and refactor SelectCategory page to use the new API
```
2025-05-12 19:24:25 +08:00
louiscklaw
650127821b ```
update nodemon config to fix watch path syntax and upgrade dependencies including sass, parcel watcher, and chokidar
```
2025-05-12 19:23:44 +08:00
louiscklaw
5440f8ea14 ``add .env to .gitignore and update ignore patterns for log, temp, and backup files`` 2025-05-12 19:23:22 +08:00
louiscklaw
f4c9dbcc34 ``add Helloworld component in Ionic mobile project`` 2025-05-12 19:23:00 +08:00
louiscklaw
f756fb8527 ``` "update tsconfig.json: adjust lib array format, simplify include syntax, and refine exclude patterns """ 2025-05-12 19:22:46 +08:00
louiscklaw
3f10a0728c ```
add authentication routes, components, and pages including AuthHome, Login, and SignUp, and implement form fields utility functions
```
2025-05-12 19:22:00 +08:00
louiscklaw
ee0aa0353b ``add development and backup scripts for Ionic mobile project, including dev script, rsync backup script, and express commit script`` 2025-05-12 19:19:39 +08:00
louiscklaw
2c7316786c ``update dc_dev.sh to remove commented logs command and adjust startup sequence for pocketbase and api_ts`` 2025-05-12 14:03:52 +08:00
louiscklaw
89f91ec2a0 ``update Notifications table schema by removing 'author' text field, adding 'author' relation, 'content', 'company', and 'link' fields, and sync changes in schema.dbml, schema.json, and pb_hooks/seed/schema.json`` 2025-05-12 14:03:35 +08:00
louiscklaw
1441863dcd ``remove obsolete UserMetas migrations and related user collection modifications`` 2025-05-12 14:01:52 +08:00
louiscklaw
1835caee68 ``update prettier config with printWidth and quoteProps, add new routes for connective revision, and update guidelines for hooks and types`` 2025-05-12 13:49:11 +08:00
louiscklaw
bac8c70d4b ``fix typos and update base URL quotes in code files`` 2025-05-12 13:48:32 +08:00
427 changed files with 8063 additions and 28496 deletions

15
.crossnote/config.js Normal file
View File

@@ -0,0 +1,15 @@
({
katexConfig: {
"macros": {}
},
mathjaxConfig: {
"tex": {},
"options": {},
"loader": {}
},
mermaidConfig: {
"startOnLoad": false
},
})

19
.crossnote/head.html Normal file
View File

@@ -0,0 +1,19 @@
<!-- The content below will be included at the end of the <head> element. -->
<script type="text/javascript">
const configureMermaidIconPacks = () => {
window['mermaid'].registerIconPacks([
{
name: 'logos',
loader: () => fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
},
]);
};
if (document.readyState !== 'loading') {
configureMermaidIconPacks();
} else {
document.addEventListener('DOMContentLoaded', () => {
configureMermaidIconPacks();
});
}
</script>

12
.crossnote/parser.js Normal file
View File

@@ -0,0 +1,12 @@
({
// Please visit the URL below for more information:
// https://shd101wyy.github.io/markdown-preview-enhanced/#/extend-parser
onWillParseMarkdown: async function(markdown) {
return markdown;
},
onDidParseMarkdown: async function(html) {
return html;
},
})

8
.crossnote/style.less Normal file
View File

@@ -0,0 +1,8 @@
/* Please visit the URL below for more information: */
/* https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */
.markdown-preview.markdown-preview {
// modify your style here
// eg: background-color: blue;
}

6
.gitignore vendored
View File

@@ -1,3 +1,6 @@
# api_ts
dist
.next
node_modules
005_references/
@@ -7,7 +10,8 @@ _del
*.bak
*.log
*.del
**/_del
**/*del
**/*old
**/volumes/**
006_lab

View File

@@ -1,12 +1,15 @@
{
"recommendations": [
"aflalo.dbml-formatter",
"bierner.markdown-mermaid",
"christian-kohler.path-intellisense",
"esbenp.prettier-vscode",
"humao.rest-client",
//
"matt-meyers.vscode-dbml",
"aflalo.dbml-formatter",
"nicolas-liger.dbml-viewer",
"yzhang.markdown-all-in-one"
//
"bierner.markdown-mermaid",
"yzhang.markdown-all-in-one",
"shd101wyy.markdown-preview-enhanced"
]
}

View File

@@ -15,7 +15,9 @@
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"workbench.iconTheme": "material-icon-theme",
"workbench.colorTheme": "Default Dark Modern",
"workbench.colorTheme": "Mermaid Dark",
"editor.formatOnSave": true,
"git.ignoreLimitWarning": true
"git.ignoreLimitWarning": true,
//
"markdown.styles": ["https://use.fontawesome.com/releases/v5.7.1/css/all.css"]
}

View File

@@ -1,11 +1,14 @@
```markdown
# Greetings
Hi,
Imaging you are a software engineer and i will send you the guideline.
Imagine you are a software engineer and i will send you the guideline.
plesae read it, prepare yourself and i will tell you the task afterwards
please read and understand the markdown files in directory
`/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/000_AI_WORKSPACE/software_engineer/greetings`,
`/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/000_AI_WORKSPACE/software-engineer/greetings`,
it provides background information of project i want you to help.
thanks
```

View File

@@ -28,5 +28,10 @@
- `006_lab` my test (POC) of this project
- `README.md` Readme of this project
- `TODO.md` todo list of this project
- `001_documentation/Requirements/REQ0019/index.md` describes updated system architecture
- if the directory contains `_GUIDELINES.md`, please read it before operation
## Abbreviations
T.B.A.

View File

@@ -1,11 +1,11 @@
import { faker } from "@faker-js/faker";
import { faker } from '@faker-js/faker';
const getId = (id) => id.padStart(15, "0");
const getId = (id) => id.padStart(15, '0');
const row_array = Array.from({ length: 10 }, (_, i) => [
getId(String(i + 1)),
faker.person.firstName(),
"",
'',
faker.internet.email(),
faker.phone.number(),
faker.company.name(),
@@ -19,13 +19,11 @@ const row_array = Array.from({ length: 10 }, (_, i) => [
},
Math.floor(Math.random() * (100 - 0 + 1)) + 0,
faker.location.timeZone(),
["en", "de", "es", "fr", "ja", "ko", "zh-CN"].sort(
() => Math.random() - 0.5
)[0],
['en', 'de', 'es', 'fr', 'ja', 'ko', 'zh-CN'].sort(() => Math.random() - 0.5)[0],
faker.finance.currencyCode(),
]);
import fs from "fs";
const filePath = "output.json";
import fs from 'fs';
const filePath = 'output.json';
fs.writeFileSync(filePath, JSON.stringify(row_array, null, 2));
console.log(`Wrote ${row_array.length} records to ${filePath}`);

View File

@@ -1,4 +1,4 @@
// Generated at: 2025-05-11T08:58:29.867Z
// Generated at: 2025-05-13T05:24:33.962Z
//
@@ -114,7 +114,6 @@ Table Notifications {
id text [pk, not null]
read boolean
type text
author text
job text
description text
NOTI_ID text
@@ -122,6 +121,10 @@ Table Notifications {
updated datetime
to_user_id integer [ref: > UserMetas.id] // relation704048736
from_user_id integer [ref: > UserMetas.id] // relation556806202
author integer [ref: > UserMetas.id] // relation3182418120
content text
company text
link varchar
}
//
@@ -203,9 +206,9 @@ Table QuizLPCategories {
cat_image file
pos integer
init_answer text
visible text
created datetime
updated datetime
visible text
slug text
remarks text
description text
@@ -220,8 +223,6 @@ Table QuizLPQuestions {
word text
sound file
cat_id integer [ref: > QuizLPCategories.id] // relation3870140739
created datetime
updated datetime
cat_name text
cat_image file
pos integer
@@ -230,6 +231,8 @@ Table QuizLPQuestions {
slug text
remarks text
description text
created datetime
updated datetime
}
//
@@ -255,9 +258,9 @@ Table QuizMFCategories {
cat_image file
pos integer
init_answer text
visible text
created datetime
updated datetime
visible text
}
//
@@ -338,17 +341,23 @@ Table Teachers {
// collection type: base
Table UserMetas {
id text [pk, not null]
helloworld text
address text
meta text
user_id integer [ref: > users.id] // relation2809058197
state text
created datetime
updated datetime
status text
avatar file
role text
name text
email text
phone text
company text
taxId text
timezone text
language text
currency text
billingAddress integer [ref: > billingAddress.id] // relation2115670734
}
//

View File

@@ -1057,16 +1057,6 @@
"system": false,
"type": "text"
},
{
"hidden": false,
"id": "json3182418120",
"maxSize": 0,
"name": "author",
"presentable": false,
"required": false,
"system": false,
"type": "json"
},
{
"hidden": false,
"id": "json4225294584",
@@ -1150,6 +1140,51 @@
"required": false,
"system": false,
"type": "relation"
},
{
"cascadeDelete": false,
"collectionId": "pbc_1305841361",
"hidden": false,
"id": "relation3182418120",
"maxSelect": 1,
"minSelect": 0,
"name": "author",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
},
{
"convertURLs": false,
"hidden": false,
"id": "editor4274335913",
"maxSize": 0,
"name": "content",
"presentable": false,
"required": false,
"system": false,
"type": "editor"
},
{
"hidden": false,
"id": "json1337919823",
"maxSize": 0,
"name": "company",
"presentable": false,
"required": false,
"system": false,
"type": "json"
},
{
"exceptDomains": null,
"hidden": false,
"id": "url917281265",
"name": "link",
"onlyDomains": null,
"presentable": false,
"required": false,
"system": false,
"type": "url"
}
],
"indexes": [],
@@ -1715,6 +1750,20 @@
"system": false,
"type": "json"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2058414169",
"max": 0,
"min": 0,
"name": "visible",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"hidden": false,
"id": "autodate2990389176",
@@ -1735,20 +1784,6 @@
"system": false,
"type": "autodate"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2058414169",
"max": 0,
"min": 0,
"name": "visible",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
@@ -1857,26 +1892,6 @@
"system": false,
"type": "relation"
},
{
"hidden": false,
"id": "autodate2990389176",
"name": "created",
"onCreate": true,
"onUpdate": false,
"presentable": false,
"system": false,
"type": "autodate"
},
{
"hidden": false,
"id": "autodate3332085495",
"name": "updated",
"onCreate": true,
"onUpdate": true,
"presentable": false,
"system": false,
"type": "autodate"
},
{
"autogeneratePattern": "",
"hidden": false,
@@ -1979,6 +1994,26 @@
"required": false,
"system": false,
"type": "editor"
},
{
"hidden": false,
"id": "autodate2990389176",
"name": "created",
"onCreate": true,
"onUpdate": false,
"presentable": false,
"system": false,
"type": "autodate"
},
{
"hidden": false,
"id": "autodate3332085495",
"name": "updated",
"onCreate": true,
"onUpdate": true,
"presentable": false,
"system": false,
"type": "autodate"
}
],
"indexes": [],
@@ -2147,6 +2182,20 @@
"system": false,
"type": "json"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2058414169",
"max": 0,
"min": 0,
"name": "visible",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"hidden": false,
"id": "autodate2990389176",
@@ -2166,20 +2215,6 @@
"presentable": false,
"system": false,
"type": "autodate"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2058414169",
"max": 0,
"min": 0,
"name": "visible",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}
],
"indexes": [],
@@ -2835,7 +2870,7 @@
"id": "text4192936109",
"max": 0,
"min": 0,
"name": "helloworld",
"name": "address",
"pattern": "",
"presentable": false,
"primaryKey": false,
@@ -2866,6 +2901,20 @@
"system": false,
"type": "relation"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2744374011",
"max": 0,
"min": 0,
"name": "state",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"hidden": false,
"id": "autodate2990389176",
@@ -2886,20 +2935,6 @@
"system": false,
"type": "autodate"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2744374011",
"max": 0,
"min": 0,
"name": "status",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"hidden": false,
"id": "file376926767",
@@ -2966,6 +3001,89 @@
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text1337919823",
"max": 0,
"min": 0,
"name": "company",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2020362641",
"max": 0,
"min": 0,
"name": "taxId",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text922858135",
"max": 0,
"min": 0,
"name": "timezone",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text3571151285",
"max": 0,
"min": 0,
"name": "language",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text1767278655",
"max": 0,
"min": 0,
"name": "currency",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"cascadeDelete": false,
"collectionId": "pbc_1509025625",
"hidden": false,
"id": "relation2115670734",
"maxSelect": 999,
"minSelect": 0,
"name": "billingAddress",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}
],
"indexes": [],
@@ -3569,11 +3687,11 @@
},
{
"id": "pbc_1509025625",
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": "",
"name": "billingAddress",
"type": "base",
"fields": [
@@ -3763,11 +3881,11 @@
},
{
"id": "pbc_2109205374",
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": "",
"name": "t1",
"type": "base",
"fields": [

View File

@@ -2,7 +2,7 @@
tags: cms, login-flow
---
# login flow
# CMS login flow
## description
@@ -31,3 +31,9 @@ graph TD;
Start((start));
End((end))
```
## test
![alt text](test.gif)
## relation

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1,43 @@
---
tags: architecture,mobile, cms, db
---
# System architecture
## Description
it should have a family photo of used framework
## Diagram
```mermaid {align="center"}
architecture-beta
group running_config(logos:aws-opsworks)[running_config]
service db(database)[pocketbase] in running_config
service tra1(internet)[incoming traffic 3000] in running_config
service cms(logos:nextjs)[next app] in running_config
service tra2(internet)[incoming traffic 5173] in running_config
service ionic(logos:ionic)[ionic app] in running_config
tra1:R --> L:cms
cms:R --> L:db
tra2:R --> L:ionic
ionic:R --> B:db
%% group planning(logos:aws-lambda)[planning]
%% service api_ts(logos:aws-lambda)[api_ts] in planning
%% service pg_db(logos:postgresql)[pg_db] in planning
%% ui:R --> L:api_ts
%% ionic:R --> L:api_ts
%% api_ts:B --> T:pg_db
%% service task_server(logos:aws-lambda)[task_server] in planning
%% api_ts:R --> L:task_server
%% service marketing(logos:wordpress-icon)[marketing] in planning
```

View File

@@ -0,0 +1,41 @@
---
tags: mobile, login-flow
---
# Mobile login flow
## description
```mermaid
graph TD;
Start-->A;
A-->B;
B-->C;
B-->D;
D-->E;
E-->F;
C-->G;
G-->A
F-->End;
A[greeting, asking username and password]
B[check if username and password is valid]
C[pasword failed]
D[pasword ok]
E[login success]
F[redirect to '/dashboard']
G[prompt user wrong username and password]
Start((start));
End((end))
```
## test
![alt text](test.gif)
### relations
[REQ0016](../REQ0016/index.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -17,5 +17,8 @@
- [REQ0013: cms dashboard](./REQ0013/index.md)
- [REQ0014: mobile client](./REQ0014/index.md)
- [REQ0015: pocketbase json schema to dbml converter](./REQ0015/index.md)
- [REQ0016: login flow](./REQ0016/index.md)
- [REQ0016: CMS login flow](./REQ0016/index.md)
- [REQ0017: lesson page documentation](./REQ0017/index.md)
- [REQ0018: family photo of frameworks](./REQ0018/index.md)
- [REQ0019: System architecture](./REQ0019/index.md)
- [REQ0020: Mobile login flow](./REQ0020/index.md)

View File

@@ -0,0 +1,11 @@
{
"folders": [
{
"path": "."
},
{
"path": "../003_test"
}
],
"settings": {}
}

Binary file not shown.

View File

@@ -58,4 +58,4 @@ NEXT_PUBLIC_MAPBOX_API_KEY=
# Google Tag Manager
NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID=
NEXT_PUBLIC_POCKETBASE_URL=http://localhost:8090
NEXT_PUBLIC_POCKETBASE_URL=http://192.168.222.199:8090

View File

@@ -82,7 +82,13 @@ module.exports = {
'eslintreact/jsx-sort-props': 'off',
'react/jsx-sort-props': 'off',
},
ignorePatterns: ['**/*del', '**/*bak', '**/*copy.*', '**/*copy*.*'],
ignorePatterns: [
'**/*.del',
'**/*.bak',
'**/*copy.*',
'**/*copy*.*',
//
],
overrides: [
{
// override to ignore no-def for `describe`, `it`, and `expect`

View File

@@ -7,7 +7,7 @@
"node": "==22"
},
"scripts": {
"dev": "next dev",
"dev": "next dev -H 0.0.0.0",
"build": "next build",
"build:w": "pnpx nodemon --ext ts,tsx,json,mjs,js,jsx --delay 15 --exec \"pnpm run build\"",
"start": "next start",

View File

@@ -28,7 +28,10 @@ const config = {
'',
'^[./]',
],
plugins: ['@ianvs/prettier-plugin-sort-imports'],
plugins: [
'@ianvs/prettier-plugin-sort-imports',
//
],
overrides: [
{
files: ['*.tsx'],

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="currentColor" d="M8 0c4.42 0 8 3.58 8 8a8.01 8.01 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38c0-.27.01-1.13.01-2.2c0-.75-.25-1.23-.54-1.48c1.78-.2 3.65-.88 3.65-3.95c0-.88-.31-1.59-.82-2.15c.08-.2.36-1.02-.08-2.12c0 0-.67-.22-2.2.82c-.64-.18-1.32-.27-2-.27s-1.36.09-2 .27c-1.53-1.03-2.2-.82-2.2-.82c-.44 1.1-.16 1.92-.08 2.12c-.51.56-.82 1.28-.82 2.15c0 3.06 1.86 3.75 3.64 3.95c-.23.2-.44.55-.51 1.07c-.46.21-1.61.55-2.33-.66c-.15-.24-.6-.83-1.23-.82c-.67.01-.27.38.01.53c.34.19.73.9.82 1.13c.16.45.68 1.31 2.69.94c0 .67.01 1.3.01 1.49c0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8"/></svg>

After

Width:  |  Height:  |  Size: 695 B

View File

@@ -0,0 +1,31 @@
{
"sign-in": "Sign in / 登入",
"dont-have-an-account": "Don't have an account ?",
"continue-with_fh": "以",
"continue-with_sh": "繼續",
"forgot-password": "Forgot password",
"user": "用戶名稱",
"password": "密碼",
"email-address": "用戶電郵",
"first-name-is-required": "名字是必填项",
"last-name-is-required": "姓氏不能为空",
"email-is-required": "邮箱不能为空",
"password-should-be-at-least-6-characters": "密码至少需要6个字符",
"you-must-accept-the-terms-and-conditions": "您必须接受条款和条件",
"sign-up": "注册",
"already-have-an-account": "已有账号?",
"or": "或",
"create-account": "创建账号",
"created-users-are-not-persisted": "已创建的用户不会被保存",
"i-have-read-the": "我已阅读",
"terms-and-conditions": "用户协议和隐私政策",
"e.g.": "例如:",
"first-name": "名",
"last-name": "姓",
"hello": "world",
"welcome-title": "Welcome to devias kit pro",
"welcome-notes": "A professional template that comes with ready-to-use MUI components developed with one common goal in mind, help you build faster & beautiful applications.",
"reset-password": "重置密码",
"back-to-login": "返回登录",
"send-recovery-link": "发送恢复链接"
}

View File

@@ -1,21 +0,0 @@
/// <reference types="jest" />
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import Page from '@/app/_helloworld/page';
// Mock the translation hook
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));
describe('Page', () => {
it('renders a heading', () => {
render(<Page hello={'world'} />);
const heading = screen.getByRole('heading', { level: 1 });
expect(heading).toBeInTheDocument();
});
});

View File

@@ -1,9 +0,0 @@
import { render } from '@testing-library/react';
// CUT = Component Under Test
import CUT from '../components/_helloworld';
it('renders homepage unchanged', () => {
const { container } = render(<CUT />);
expect(container).toMatchSnapshot();
});

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
// import type { LessonCategory } from '@/components/dashboard/lp_categories/type';

View File

@@ -28,12 +28,13 @@ import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
import { ShieldWarning as ShieldWarningIcon } from '@phosphor-icons/react/dist/ssr/ShieldWarning';
import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User';
import { RecordModel } from 'pocketbase';
import type { RecordModel } from 'pocketbase';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { dayjs } from '@/lib/dayjs';
import { logger } from '@/lib/default-logger';
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import { pb } from '@/lib/pb';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
@@ -45,7 +46,7 @@ import { Notifications } from '@/components/dashboard/lesson_category/notificati
import { Payments } from '@/components/dashboard/lesson_category/payments';
import type { Address } from '@/components/dashboard/lesson_category/shipping-address';
import { ShippingAddress } from '@/components/dashboard/lesson_category/shipping-address';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
// import type { LessonCategory } from '@/components/dashboard/lp_categories/type';
import FormLoading from '@/components/loading';
@@ -119,39 +120,72 @@ export default function Page(): React.JSX.Element {
{t('dashboard.lessonCategorys.list.title')}
</Link>
</div>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={3} sx={{ alignItems: 'flex-start' }}>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: '1 1 auto' }}>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={3}
sx={{ alignItems: 'flex-start' }}
>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flex: '1 1 auto' }}
>
<Avatar
src={`http://127.0.0.1:8090/api/files/${showLessonCategory.collectionId}/${showLessonCategory.id}/${showLessonCategory.cat_image}`}
src={getImageUrlFromFile(
showLessonCategory.collectionId,
showLessonCategory.id,
showLessonCategory.cat_image
)}
sx={{ '--Avatar-size': '64px' }}
variant="rounded"
>
empty
</Avatar>
<div>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flexWrap: 'wrap' }}>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flexWrap: 'wrap' }}
>
<Typography variant="h4">{showLessonCategory.name}</Typography>
<Chip
icon={<CheckCircleIcon color="var(--mui-palette-success-main)" weight="fill" />}
icon={
<CheckCircleIcon
color="var(--mui-palette-success-main)"
weight="fill"
/>
}
label={showLessonCategory.visible}
size="small"
variant="outlined"
/>
</Stack>
<Typography color="text.secondary" variant="body1">
<Typography
color="text.secondary"
variant="body1"
>
{showLessonCategory.id}
</Typography>
</div>
</Stack>
<div>
<Button endIcon={<CaretDownIcon />} variant="contained">
<Button
endIcon={<CaretDownIcon />}
variant="contained"
>
Action
</Button>
</div>
</Stack>
</Stack>
<Grid container spacing={4}>
<Grid lg={4} xs={12}>
<Grid
container
spacing={4}
>
<Grid
lg={4}
xs={12}
>
<Stack spacing={4}>
<Card>
<CardHeader
@@ -178,7 +212,16 @@ export default function Page(): React.JSX.Element {
>
{(
[
{ key: 'Customer ID', value: <Chip label={showLessonCategory.id} size="small" variant="soft" /> },
{
key: 'Customer ID',
value: (
<Chip
label={showLessonCategory.id}
size="small"
variant="soft"
/>
),
},
{ key: 'Name', value: showLessonCategory.name },
{ key: 'Pos', value: showLessonCategory.pos },
{
@@ -195,9 +238,20 @@ export default function Page(): React.JSX.Element {
{
key: 'Quota',
value: (
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
<LinearProgress sx={{ flex: '1 1 auto' }} value={50} variant="determinate" />
<Typography color="text.secondary" variant="body2">
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center' }}
>
<LinearProgress
sx={{ flex: '1 1 auto' }}
value={50}
variant="determinate"
/>
<Typography
color="text.secondary"
variant="body2"
>
50%
</Typography>
</Stack>
@@ -206,7 +260,11 @@ export default function Page(): React.JSX.Element {
] satisfies { key: string; value: React.ReactNode }[]
).map(
(item): React.JSX.Element => (
<PropertyItem key={item.key} name={item.key} value={item.value} />
<PropertyItem
key={item.key}
name={item.key}
value={item.value}
/>
)
)}
</PropertyList>
@@ -223,11 +281,17 @@ export default function Page(): React.JSX.Element {
<CardContent>
<Stack spacing={1}>
<div>
<Button color="error" variant="contained">
<Button
color="error"
variant="contained"
>
Delete account
</Button>
</div>
<Typography color="text.secondary" variant="body2">
<Typography
color="text.secondary"
variant="body2"
>
A deleted lesson category cannot be restored. All data will be permanently removed.
</Typography>
</Stack>
@@ -235,7 +299,10 @@ export default function Page(): React.JSX.Element {
</Card>
</Stack>
</Grid>
<Grid lg={8} xs={12}>
<Grid
lg={8}
xs={12}
>
<Stack spacing={4}>
<Payments
ordersValue={2069.48}
@@ -282,7 +349,10 @@ export default function Page(): React.JSX.Element {
<Card>
<CardHeader
action={
<Button color="secondary" startIcon={<PencilSimpleIcon />}>
<Button
color="secondary"
startIcon={<PencilSimpleIcon />}
>
Edit
</Button>
}
@@ -294,8 +364,14 @@ export default function Page(): React.JSX.Element {
title={t('billing-details', { ns: 'lesson_category' })}
/>
<CardContent>
<Card sx={{ borderRadius: 1 }} variant="outlined">
<PropertyList divider={<Divider />} sx={{ '--PropertyItem-padding': '16px' }}>
<Card
sx={{ borderRadius: 1 }}
variant="outlined"
>
<PropertyList
divider={<Divider />}
sx={{ '--PropertyItem-padding': '16px' }}
>
{(
[
{ key: 'Credit card', value: '**** 4142' },
@@ -307,7 +383,11 @@ export default function Page(): React.JSX.Element {
] satisfies { key: string; value: React.ReactNode }[]
).map(
(item): React.JSX.Element => (
<PropertyItem key={item.key} name={item.key} value={item.value} />
<PropertyItem
key={item.key}
name={item.key}
value={item.value}
/>
)
)}
</PropertyList>
@@ -317,7 +397,10 @@ export default function Page(): React.JSX.Element {
<Card>
<CardHeader
action={
<Button color="secondary" startIcon={<PlusIcon />}>
<Button
color="secondary"
startIcon={<PlusIcon />}
>
Add
</Button>
}
@@ -329,7 +412,10 @@ export default function Page(): React.JSX.Element {
title={t('shipping-addresses', { ns: 'lesson_category' })}
/>
<CardContent>
<Grid container spacing={3}>
<Grid
container
spacing={3}
>
{(
[
{
@@ -351,7 +437,11 @@ export default function Page(): React.JSX.Element {
},
] satisfies Address[]
).map((address) => (
<Grid key={address.id} md={6} xs={12}>
<Grid
key={address.id}
md={6}
xs={12}
>
<ShippingAddress address={address} />
</Grid>
))}

View File

@@ -90,8 +90,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
React.useEffect(() => {
if (!isFirstRun.current) {
isFirstRun.current = true;
} else {
if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
} else if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
// reset page number as tab changes
setLastListOption(listOption);
setCurrentPage(0);
@@ -99,7 +98,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
} else {
void reloadRows();
}
}
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
export const LpCategoriesSampleData = [
{

View File

@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
import { CrCategory } from '@/components/dashboard/cr/categories/type';
import type { CrCategory } from '@/components/dashboard/cr/categories/type';
export default function BasicDetailCard({
lpModel: model,

View File

@@ -10,12 +10,9 @@ import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/Caret
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { CrCategory } from '@/components/dashboard/cr/categories/type';
function getImageUrlFrRecord(record: CrCategory): string {
return `http://127.0.0.1:8090/api/files/${record.collectionId}/${record.id}/${record.cat_image}`;
}
export default function SampleTitleCard({ lpModel }: { lpModel: CrCategory }): React.JSX.Element {
const { t } = useTranslation();
@@ -28,7 +25,7 @@ export default function SampleTitleCard({ lpModel }: { lpModel: CrCategory }): R
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
src={getImageUrlFromFile(lpModel.collectionId, lpModel.id, lpModel.cat_image)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
import { LpCategory } from '@/components/dashboard/lp/categories/type';
import type { LpCategory } from '@/components/dashboard/lp/categories/type';
export default function BasicDetailCard({
lpModel: model,

View File

@@ -10,11 +10,8 @@ import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/Caret
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import { LpCategory } from '@/components/dashboard/lp/categories/type';
function getImageUrlFrRecord(record: LpCategory): string {
return `http://127.0.0.1:8090/api/files/${record.collectionId}/${record.id}/${record.cat_image}`;
}
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { LpCategory } from '@/components/dashboard/lp/categories/type';
export default function SampleTitleCard({ lpModel }: { lpModel: LpCategory }): React.JSX.Element {
const { t } = useTranslation();
@@ -28,7 +25,7 @@ export default function SampleTitleCard({ lpModel }: { lpModel: LpCategory }): R
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
src={getImageUrlFromFile(lpModel.collectionId, lpModel.id, lpModel.cat_image)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
export const CrCategoriesSampleData = [
{

View File

@@ -9,14 +9,14 @@ import Typography from '@mui/material/Typography';
import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown';
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import type { Customer } from '@/components/dashboard/customer/type.d';
// import type { CrCategory } from '@/components/dashboard/cr/categories/type';
function getImageUrlFrRecord(record: Customer): string {
// TODO: fix this
// `http://127.0.0.1:8090/api/files/${'record.collectionId'}/${'record.id'}/${'record.cat_image'}`;
return 'getImageUrlFrRecord(helloworld)';
// TODO: implement getImageUrlFrRecord
return 'not implemented';
}
export default function SampleTitleCard({ lpModel }: { lpModel: Customer }): React.JSX.Element {

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
// import type { LessonCategory } from '@/components/dashboard/lp_categories/type';

View File

@@ -28,12 +28,13 @@ import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
import { ShieldWarning as ShieldWarningIcon } from '@phosphor-icons/react/dist/ssr/ShieldWarning';
import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User';
import { RecordModel } from 'pocketbase';
import type { RecordModel } from 'pocketbase';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { dayjs } from '@/lib/dayjs';
import { logger } from '@/lib/default-logger';
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import { pb } from '@/lib/pb';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
@@ -45,7 +46,7 @@ import { Notifications } from '@/components/dashboard/lesson_category/notificati
import { Payments } from '@/components/dashboard/lesson_category/payments';
import type { Address } from '@/components/dashboard/lesson_category/shipping-address';
import { ShippingAddress } from '@/components/dashboard/lesson_category/shipping-address';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
// import type { LessonCategory } from '@/components/dashboard/lp_categories/type';
import FormLoading from '@/components/loading';
@@ -119,39 +120,72 @@ export default function Page(): React.JSX.Element {
{t('dashboard.lessonCategorys.list.title')}
</Link>
</div>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={3} sx={{ alignItems: 'flex-start' }}>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: '1 1 auto' }}>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={3}
sx={{ alignItems: 'flex-start' }}
>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flex: '1 1 auto' }}
>
<Avatar
src={`http://127.0.0.1:8090/api/files/${showLessonCategory.collectionId}/${showLessonCategory.id}/${showLessonCategory.cat_image}`}
src={getImageUrlFromFile(
showLessonCategory.collectionId,
showLessonCategory.id,
showLessonCategory.cat_image
)}
sx={{ '--Avatar-size': '64px' }}
variant="rounded"
>
empty
</Avatar>
<div>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flexWrap: 'wrap' }}>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flexWrap: 'wrap' }}
>
<Typography variant="h4">{showLessonCategory.name}</Typography>
<Chip
icon={<CheckCircleIcon color="var(--mui-palette-success-main)" weight="fill" />}
icon={
<CheckCircleIcon
color="var(--mui-palette-success-main)"
weight="fill"
/>
}
label={showLessonCategory.visible}
size="small"
variant="outlined"
/>
</Stack>
<Typography color="text.secondary" variant="body1">
<Typography
color="text.secondary"
variant="body1"
>
{showLessonCategory.id}
</Typography>
</div>
</Stack>
<div>
<Button endIcon={<CaretDownIcon />} variant="contained">
<Button
endIcon={<CaretDownIcon />}
variant="contained"
>
Action
</Button>
</div>
</Stack>
</Stack>
<Grid container spacing={4}>
<Grid lg={4} xs={12}>
<Grid
container
spacing={4}
>
<Grid
lg={4}
xs={12}
>
<Stack spacing={4}>
<Card>
<CardHeader
@@ -178,7 +212,16 @@ export default function Page(): React.JSX.Element {
>
{(
[
{ key: 'Customer ID', value: <Chip label={showLessonCategory.id} size="small" variant="soft" /> },
{
key: 'Customer ID',
value: (
<Chip
label={showLessonCategory.id}
size="small"
variant="soft"
/>
),
},
{ key: 'Name', value: showLessonCategory.name },
{ key: 'Pos', value: showLessonCategory.pos },
{
@@ -195,9 +238,20 @@ export default function Page(): React.JSX.Element {
{
key: 'Quota',
value: (
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
<LinearProgress sx={{ flex: '1 1 auto' }} value={50} variant="determinate" />
<Typography color="text.secondary" variant="body2">
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center' }}
>
<LinearProgress
sx={{ flex: '1 1 auto' }}
value={50}
variant="determinate"
/>
<Typography
color="text.secondary"
variant="body2"
>
50%
</Typography>
</Stack>
@@ -206,7 +260,11 @@ export default function Page(): React.JSX.Element {
] satisfies { key: string; value: React.ReactNode }[]
).map(
(item): React.JSX.Element => (
<PropertyItem key={item.key} name={item.key} value={item.value} />
<PropertyItem
key={item.key}
name={item.key}
value={item.value}
/>
)
)}
</PropertyList>
@@ -223,11 +281,17 @@ export default function Page(): React.JSX.Element {
<CardContent>
<Stack spacing={1}>
<div>
<Button color="error" variant="contained">
<Button
color="error"
variant="contained"
>
Delete account
</Button>
</div>
<Typography color="text.secondary" variant="body2">
<Typography
color="text.secondary"
variant="body2"
>
A deleted lesson category cannot be restored. All data will be permanently removed.
</Typography>
</Stack>
@@ -235,7 +299,10 @@ export default function Page(): React.JSX.Element {
</Card>
</Stack>
</Grid>
<Grid lg={8} xs={12}>
<Grid
lg={8}
xs={12}
>
<Stack spacing={4}>
<Payments
ordersValue={2069.48}
@@ -282,7 +349,10 @@ export default function Page(): React.JSX.Element {
<Card>
<CardHeader
action={
<Button color="secondary" startIcon={<PencilSimpleIcon />}>
<Button
color="secondary"
startIcon={<PencilSimpleIcon />}
>
Edit
</Button>
}
@@ -294,8 +364,14 @@ export default function Page(): React.JSX.Element {
title={t('billing-details', { ns: 'lesson_category' })}
/>
<CardContent>
<Card sx={{ borderRadius: 1 }} variant="outlined">
<PropertyList divider={<Divider />} sx={{ '--PropertyItem-padding': '16px' }}>
<Card
sx={{ borderRadius: 1 }}
variant="outlined"
>
<PropertyList
divider={<Divider />}
sx={{ '--PropertyItem-padding': '16px' }}
>
{(
[
{ key: 'Credit card', value: '**** 4142' },
@@ -307,7 +383,11 @@ export default function Page(): React.JSX.Element {
] satisfies { key: string; value: React.ReactNode }[]
).map(
(item): React.JSX.Element => (
<PropertyItem key={item.key} name={item.key} value={item.value} />
<PropertyItem
key={item.key}
name={item.key}
value={item.value}
/>
)
)}
</PropertyList>
@@ -317,7 +397,10 @@ export default function Page(): React.JSX.Element {
<Card>
<CardHeader
action={
<Button color="secondary" startIcon={<PlusIcon />}>
<Button
color="secondary"
startIcon={<PlusIcon />}
>
Add
</Button>
}
@@ -329,7 +412,10 @@ export default function Page(): React.JSX.Element {
title={t('shipping-addresses', { ns: 'lesson_category' })}
/>
<CardContent>
<Grid container spacing={3}>
<Grid
container
spacing={3}
>
{(
[
{
@@ -351,7 +437,11 @@ export default function Page(): React.JSX.Element {
},
] satisfies Address[]
).map((address) => (
<Grid key={address.id} md={6} xs={12}>
<Grid
key={address.id}
md={6}
xs={12}
>
<ShippingAddress address={address} />
</Grid>
))}

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonType } from '@/components/dashboard/lesson_type/lesson-type';
import type { LessonType } from '@/components/dashboard/lesson_type/lesson-type';
// import type { LessonType } from '@/components/dashboard/lesson_type/ILessonType';

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonType } from '@/components/dashboard/lesson_type/lesson-type';
import type { LessonType } from '@/components/dashboard/lesson_type/lesson-type';
// import type { LessonType } from '@/components/dashboard/lesson_type/ILessonType';

View File

@@ -88,8 +88,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
React.useEffect(() => {
if (!isFirstRun.current) {
isFirstRun.current = true;
} else {
if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
} else if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
// reset page number as tab changes
setLastListOption(listOption);
setCurrentPage(0);
@@ -97,7 +96,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
} else {
void reloadRows();
}
}
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
export const LpCategoriesSampleData = [
{

View File

@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
import { LpCategory } from '@/components/dashboard/lp/categories/type';
import type { LpCategory } from '@/components/dashboard/lp/categories/type';
export default function BasicDetailCard({
lpModel: model,

View File

@@ -10,11 +10,8 @@ import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/Caret
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import { LpCategory } from '@/components/dashboard/lp/categories/type';
function getImageUrlFrRecord(record: LpCategory): string {
return `http://127.0.0.1:8090/api/files/${record.collectionId}/${record.id}/${record.cat_image}`;
}
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { LpCategory } from '@/components/dashboard/lp/categories/type';
export default function SampleTitleCard({ lpModel }: { lpModel: LpCategory }): React.JSX.Element {
const { t } = useTranslation();
@@ -28,7 +25,7 @@ export default function SampleTitleCard({ lpModel }: { lpModel: LpCategory }): R
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
src={getImageUrlFromFile(lpModel.collectionId, lpModel.id, lpModel.cat_image)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -88,8 +88,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
React.useEffect(() => {
if (!isFirstRun.current) {
isFirstRun.current = true;
} else {
if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
} else if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
// reset page number as tab changes
setLastListOption(listOption);
setCurrentPage(0);
@@ -97,7 +96,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
} else {
void reloadRows();
}
}
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
export const LpCategoriesSampleData = [
{

View File

@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
import { LpCategory } from '@/components/dashboard/lp/categories/type';
import type { LpCategory } from '@/components/dashboard/lp/categories/type';
export default function BasicDetailCard({
lpModel: model,

View File

@@ -10,11 +10,8 @@ import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/Caret
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import { LpCategory } from '@/components/dashboard/lp/categories/type';
function getImageUrlFrRecord(record: LpCategory): string {
return `http://127.0.0.1:8090/api/files/${record.collectionId}/${record.id}/${record.cat_image}`;
}
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { LpCategory } from '@/components/dashboard/lp/categories/type';
export default function SampleTitleCard({ lpModel }: { lpModel: LpCategory }): React.JSX.Element {
const { t } = useTranslation();
@@ -28,7 +25,7 @@ export default function SampleTitleCard({ lpModel }: { lpModel: LpCategory }): R
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
src={getImageUrlFromFile(lpModel.collectionId, lpModel.id, lpModel.cat_image)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -86,8 +86,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
React.useEffect(() => {
if (!isFirstRun.current) {
isFirstRun.current = true;
} else {
if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
} else if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
// reset page number as tab changes
setLastListOption(listOption);
setCurrentPage(0);
@@ -95,7 +94,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
} else {
void reloadRows();
}
}
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
export const LpCategoriesSampleData = [
{

View File

@@ -10,12 +10,9 @@ import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/Caret
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { MfCategory } from '@/components/dashboard/mf/categories/type';
function getImageUrlFrRecord(record: MfCategory): string {
return `http://127.0.0.1:8090/api/files/${record.collectionId}/${record.id}/${record.cat_image}`;
}
export default function SampleTitleCard({ lpModel }: { lpModel: MfCategory }): React.JSX.Element {
const { t } = useTranslation();
@@ -28,7 +25,7 @@ export default function SampleTitleCard({ lpModel }: { lpModel: MfCategory }): R
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
src={getImageUrlFromFile(lpModel.collectionId, lpModel.id, lpModel.cat_image)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -86,8 +86,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
React.useEffect(() => {
if (!isFirstRun.current) {
isFirstRun.current = true;
} else {
if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
} else if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
// reset page number as tab changes
setLastListOption(listOption);
setCurrentPage(0);
@@ -95,7 +94,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
} else {
void reloadRows();
}
}
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
export const LpCategoriesSampleData = [
{

View File

@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
import { MfCategory } from '@/components/dashboard/mf/categories/type';
import type { MfCategory } from '@/components/dashboard/mf/categories/type';
export default function BasicDetailCard({
lpModel: model,

View File

@@ -10,12 +10,9 @@ import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/Caret
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { MfCategory } from '@/components/dashboard/mf/categories/type';
function getImageUrlFrRecord(record: MfCategory): string {
return `http://127.0.0.1:8090/api/files/${record.collectionId}/${record.id}/${record.cat_image}`;
}
export default function SampleTitleCard({ lpModel }: { lpModel: MfCategory }): React.JSX.Element {
const { t } = useTranslation();
@@ -28,7 +25,7 @@ export default function SampleTitleCard({ lpModel }: { lpModel: MfCategory }): R
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
src={getImageUrlFromFile(lpModel.collectionId, lpModel.id, lpModel.cat_image)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -1,19 +1,25 @@
'use client';
// src/app/dashboard/students/create/page.tsx
// PURPOSE
// T.B.A.
//
import * as React from 'react';
import type { Metadata } from 'next';
import RouterLink from 'next/link';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft';
import { useTranslation } from 'react-i18next';
import { config } from '@/config';
import { paths } from '@/paths';
import { CustomerCreateForm } from '@/components/dashboard/student/student-create-form';
export const metadata = { title: `Create | Customers | Dashboard | ${config.site.name}` } satisfies Metadata;
import { StudentCreateForm } from '@/components/dashboard/student/student-create-form';
export default function Page(): React.JSX.Element {
const { t } = useTranslation(['students']);
return (
<Box
sx={{
@@ -29,19 +35,22 @@ export default function Page(): React.JSX.Element {
<Link
color="text.primary"
component={RouterLink}
href={paths.dashboard.customers.list}
href={paths.dashboard.students.list}
sx={{ alignItems: 'center', display: 'inline-flex', gap: 1 }}
variant="subtitle2"
>
<ArrowLeftIcon fontSize="var(--icon-fontSize-md)" />
Customers
{t('students')}
</Link>
</div>
<div>
<Typography variant="h4">Create customer</Typography>
<Typography variant="h4">
{t('create-student')}
{/* */}
</Typography>
</div>
</Stack>
<CustomerCreateForm />
<StudentCreateForm />
</Stack>
</Box>
);

View File

@@ -1 +0,0 @@
this `tsx` file is clone from elsewhere, please understand, modify and update the content of `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/app/dashboard/students/edit/[customerId]/page.tsx.draft` to handle `Student` record thanks, modify comments/variables/paths/functions name please

View File

@@ -0,0 +1,6 @@
this `tsx` file is clone from elsewhere,
please understand, modify and update the content of
`/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/app/dashboard/students/edit/[customerId]/page.tsx.draft`
to handle `Student` record thanks,
modify comments/variables/paths/functions name please

View File

@@ -1,6 +1,9 @@
'use client';
// src/app/dashboard/students/edit/[customerId]/page.tsx
// src/app/dashboard/students/edit/[id]/page.tsx
// PURPOSE
// T.B.A.
//
import * as React from 'react';
import RouterLink from 'next/link';
import Box from '@mui/material/Box';
@@ -11,7 +14,8 @@ import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/Arrow
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { CrCategoryEditForm } from '@/components/dashboard/cr/categories/cr-category-edit-form';
// TODO: remove me
// import { CrCategoryEditForm } from '@/components/dashboard/cr/categories/cr-category-edit-form';
import { StudentEditForm } from '@/components/dashboard/student/student-edit-form';
export default function Page(): React.JSX.Element {

View File

@@ -1,6 +1,7 @@
// src/app/dashboard/students/list/page.tsx
'use client';
// src/app/dashboard/students/list/page.tsx
//
// RULES:
// contains list page for students (Students)
//
@@ -15,12 +16,6 @@ import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
import type { ListResult, RecordModel } from 'pocketbase';
import { StudentsFilters } from '@/components/dashboard/student/students-filters';
import { StudentsPagination } from '@/components/dashboard/student/students-pagination';
import { StudentsSelectionProvider } from '@/components/dashboard/student/students-selection-context';
import { StudentsTable } from '@/components/dashboard/student/students-table';
import type { Student } from '@/components/dashboard/student/type.d';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
@@ -29,13 +24,18 @@ import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import ErrorDisplay from '@/components/dashboard/error';
import { defaultStudent } from '@/components/dashboard/student/_constants';
import { StudentsFilters } from '@/components/dashboard/student/students-filters';
import { StudentsPagination } from '@/components/dashboard/student/students-pagination';
import { StudentsSelectionProvider } from '@/components/dashboard/student/students-selection-context';
import { StudentsTable } from '@/components/dashboard/student/students-table';
import type { Student } from '@/components/dashboard/student/type.d';
import FormLoading from '@/components/loading';
export default function Page({ searchParams }: PageProps): React.JSX.Element {
const { t } = useTranslation(['students']);
const router = useRouter();
const { email, phone, sortDir, status } = searchParams;
const { email, phone, sortDir, state } = searchParams;
const [studentsData, setStudentsData] = React.useState<Student[]>([]);
@@ -104,8 +104,8 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
const tempFilter = [];
let tempSortDir = '';
if (status) {
tempFilter.push(`status = "${status}"`);
if (state) {
tempFilter.push(`state = "${state}"`);
}
if (sortDir) {
@@ -128,7 +128,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
preFinalListOption = { ...preFinalListOption, sort: tempSortDir };
}
setListOption(preFinalListOption);
}, [sortDir, email, phone, status]);
}, [sortDir, email, phone, state]);
if (showLoading) return <FormLoading />;
@@ -176,7 +176,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
<StudentsSelectionProvider students={f}>
<Card>
<StudentsFilters
filters={{ email, phone, status }}
filters={{ email, phone, status: state }}
fullData={studentsData}
sortDir={sortDir}
/>
@@ -210,7 +210,7 @@ interface PageProps {
email?: string;
phone?: string;
sortDir?: 'asc' | 'desc';
status?: string;
state?: string;
//
};
}

View File

@@ -9,14 +9,10 @@ import Typography from '@mui/material/Typography';
import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown';
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { Student } from '@/components/dashboard/student/type.d';
// import type { CrCategory } from '@/components/dashboard/cr/categories/type';
function getImageUrlForStudent(student: Student): string {
return `http://127.0.0.1:8090/api/files/${student.collectionId}/${student.id}/${student.avatar}`;
}
export default function SampleTitleCard({ studentRecord }: { studentRecord: Student }): React.JSX.Element {
const { t } = useTranslation();
@@ -29,7 +25,7 @@ export default function SampleTitleCard({ studentRecord }: { studentRecord: Stud
>
<Avatar
variant="rounded"
src={getImageUrlForStudent(studentRecord)}
src={getImageUrlFromFile(studentRecord.collectionId, studentRecord.id, studentRecord.avatar)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -1,80 +0,0 @@
'use client';
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import Card from '@mui/material/Card';
import CardHeader from '@mui/material/CardHeader';
import Chip from '@mui/material/Chip';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple';
import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User';
import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
// import { CrCategory } from '@/components/dashboard/cr/categories/type';
import type { Student } from '@/components/dashboard/student/type.d';
export default function BasicDetailCard({
lpModel: model,
handleEditClick,
}: {
lpModel: Student;
handleEditClick: () => void;
}): React.JSX.Element {
const { t } = useTranslation();
return (
<Card>
<CardHeader
action={
<IconButton
onClick={() => {
handleEditClick();
}}
>
<PencilSimpleIcon />
</IconButton>
}
avatar={
<Avatar>
<UserIcon fontSize="var(--Icon-fontSize)" />
</Avatar>
}
title={t('list.basic-details')}
/>
<PropertyList
divider={<Divider />}
orientation="vertical"
sx={{ '--PropertyItem-padding': '12px 24px' }}
>
{(
[
{
key: 'Customer ID',
value: (
<Chip
label={model.id}
size="small"
variant="soft"
/>
),
},
{ key: 'Email', value: model.email },
{ key: 'Quota', value: model.quota },
{ key: 'Status', value: model.status },
] satisfies { key: string; value: React.ReactNode }[]
).map(
(item): React.JSX.Element => (
<PropertyItem
key={item.key}
name={item.key}
value={item.value}
/>
)
)}
</PropertyList>
</Card>
);
}

View File

@@ -1,76 +0,0 @@
'use client';
import * as React from 'react';
import { Button } from '@mui/material';
import Avatar from '@mui/material/Avatar';
import Chip from '@mui/material/Chip';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown';
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import type { Student } from '@/components/dashboard/student/type.d';
// import type { CrCategory } from '@/components/dashboard/cr/categories/type';
function getImageUrlFrRecord(record: Student): string {
// TODO: fix this
// `http://127.0.0.1:8090/api/files/${'record.collectionId'}/${'record.id'}/${'record.cat_image'}`;
return 'getImageUrlFrRecord(helloworld)';
}
export default function SampleTitleCard({ lpModel }: { lpModel: Student }): React.JSX.Element {
const { t } = useTranslation();
return (
<>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flex: '1 1 auto' }}
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}
</Avatar>
<div>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flexWrap: 'wrap' }}
>
<Typography variant="h4">{lpModel.email}</Typography>
<Chip
icon={
<CheckCircleIcon
color="var(--mui-palette-success-main)"
weight="fill"
/>
}
label={lpModel.quota}
size="small"
variant="outlined"
/>
</Stack>
<Typography
color="text.secondary"
variant="body1"
>
{lpModel.status}
</Typography>
</div>
</Stack>
<div>
<Button
endIcon={<CaretDownIcon />}
variant="contained"
>
{t('list.action')}
</Button>
</div>
</>
);
}

View File

@@ -1,142 +0,0 @@
'use client';
import * as React from 'react';
import RouterLink from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import SampleAddressCard from '@/app/dashboard/Sample/AddressCard';
import { SampleNotifications } from '@/app/dashboard/Sample/Notifications';
import SamplePaymentCard from '@/app/dashboard/Sample/PaymentCard';
import SampleSecurityCard from '@/app/dashboard/Sample/SecurityCard';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import Grid from '@mui/material/Unstable_Grid2';
import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft';
import type { RecordModel } from 'pocketbase';
import { useTranslation } from 'react-i18next';
import { config } from '@/config';
import { paths } from '@/paths';
import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import { toast } from '@/components/core/toaster';
import ErrorDisplay from '@/components/dashboard/error';
import { Notifications } from '@/components/dashboard/student/notifications';
import FormLoading from '@/components/loading';
import BasicDetailCard from './BasicDetailCard';
import TitleCard from './TitleCard';
import { defaultStudent } from '@/components/dashboard/student/_constants';
import type { Student } from '@/components/dashboard/student/type.d';
import { COL_STUDENTS } from '@/constants';
export default function Page(): React.JSX.Element {
const { t } = useTranslation();
const router = useRouter();
//
const { customerId } = useParams<{ customerId: string }>();
//
const [showLoading, setShowLoading] = React.useState<boolean>(true);
const [showError, setShowError] = React.useState({ show: false, detail: '' });
//
const [showLessonCategory, setShowLessonCategory] = React.useState<Student>(defaultStudent);
function handleEditClick(): void {
router.push(paths.dashboard.students.edit(showLessonCategory.id));
}
React.useEffect(() => {
if (customerId) {
pb.collection(COL_STUDENTS)
.getOne(customerId)
.then((model: RecordModel) => {
setShowLessonCategory({ ...defaultStudent, ...model });
})
.catch((err) => {
logger.error(err);
toast(t('list.error'));
setShowError({ show: true, detail: JSON.stringify(err) });
})
.finally(() => {
setShowLoading(false);
});
}
}, [customerId]);
// return <>{JSON.stringify({ showError, showLessonCategory }, null, 2)}</>;
if (showLoading) return <FormLoading />;
if (showError.show)
return (
<ErrorDisplay
message={t('error.unable-to-process-request')}
code="500"
details={showError.detail}
/>
);
return (
<Box
sx={{
maxWidth: 'var(--Content-maxWidth)',
m: 'var(--Content-margin)',
p: 'var(--Content-padding)',
width: 'var(--Content-width)',
}}
>
<Stack spacing={4}>
<Stack spacing={3}>
<div>
<Link
color="text.primary"
component={RouterLink}
href={paths.dashboard.students.list}
sx={{ alignItems: 'center', display: 'inline-flex', gap: 1 }}
variant="subtitle2"
>
<ArrowLeftIcon fontSize="var(--icon-fontSize-md)" />
Students
</Link>
</div>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={3}
sx={{ alignItems: 'flex-start' }}
>
<TitleCard lpModel={showLessonCategory} />
</Stack>
</Stack>
<Grid
container
spacing={4}
>
<Grid
lg={4}
xs={12}
>
<Stack spacing={4}>
<BasicDetailCard
lpModel={showLessonCategory}
handleEditClick={handleEditClick}
/>
<SampleSecurityCard />
</Stack>
</Grid>
<Grid
lg={8}
xs={12}
>
<Stack spacing={4}>
<SamplePaymentCard />
<SampleAddressCard />
<Notifications notifications={SampleNotifications} />
</Stack>
</Grid>
</Grid>
</Stack>
</Box>
);
}

View File

@@ -1,4 +1,9 @@
'use client';
// src/app/dashboard/teachers/create/page.tsx
// PURPOSE
// T.B.A.
//
import * as React from 'react';
import RouterLink from 'next/link';
import Box from '@mui/material/Box';
@@ -6,12 +11,15 @@ import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft';
import { useTranslation } from 'react-i18next';
import { config } from '@/config';
import { paths } from '@/paths';
import { TeacherCreateForm } from '@/components/dashboard/teacher/teacher-create-form';
export default function Page(): React.JSX.Element {
const { t } = useTranslation(['teachers']);
return (
<Box
sx={{
@@ -32,11 +40,14 @@ export default function Page(): React.JSX.Element {
variant="subtitle2"
>
<ArrowLeftIcon fontSize="var(--icon-fontSize-md)" />
Teachers
{t('teachers')}
</Link>
</div>
<div>
<Typography variant="h4">Create teacher</Typography>
<Typography variant="h4">
{t('create-teacher')}
{/* */}
</Typography>
</div>
</Stack>
<TeacherCreateForm />

View File

@@ -1,5 +1,9 @@
'use client';
// src/app/dashboard/teachers/edit/[id]/page.tsx
// PURPOSE
// T.B.A.
//
import * as React from 'react';
import RouterLink from 'next/link';
import Box from '@mui/material/Box';
@@ -10,7 +14,8 @@ import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/Arrow
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { CrCategoryEditForm } from '@/components/dashboard/cr/categories/cr-category-edit-form';
// TODO: remove me
// import { CrCategoryEditForm } from '@/components/dashboard/cr/categories/cr-category-edit-form';
import { TeacherEditForm } from '@/components/dashboard/teacher/teacher-edit-form';
export default function Page(): React.JSX.Element {

View File

@@ -1,6 +1,7 @@
// src/app/dashboard/teachers/list/page.tsx
'use client';
// src/app/dashboard/teachers/list/page.tsx
//
// RULES:
// contains list page for teachers (Teachers)
//
@@ -15,12 +16,6 @@ import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
import type { ListResult, RecordModel } from 'pocketbase';
import { TeachersFilters } from '@/components/dashboard/teacher/teachers-filters';
import { TeachersPagination } from '@/components/dashboard/teacher/teachers-pagination';
import { TeachersSelectionProvider } from '@/components/dashboard/teacher/teachers-selection-context';
import { TeachersTable } from '@/components/dashboard/teacher/teachers-table';
import type { Teacher } from '@/components/dashboard/teacher/type.d';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
@@ -29,13 +24,18 @@ import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import ErrorDisplay from '@/components/dashboard/error';
import { defaultTeacher } from '@/components/dashboard/teacher/_constants';
import { TeachersFilters } from '@/components/dashboard/teacher/teachers-filters';
import { TeachersPagination } from '@/components/dashboard/teacher/teachers-pagination';
import { TeachersSelectionProvider } from '@/components/dashboard/teacher/teachers-selection-context';
import { TeachersTable } from '@/components/dashboard/teacher/teachers-table';
import type { Teacher } from '@/components/dashboard/teacher/type.d';
import FormLoading from '@/components/loading';
export default function Page({ searchParams }: PageProps): React.JSX.Element {
const { t } = useTranslation(['teachers']);
const router = useRouter();
const { email, phone, sortDir, status } = searchParams;
const { email, phone, sortDir, state } = searchParams;
const [teacherData, setTeacherData] = React.useState<Teacher[]>([]);
@@ -103,11 +103,11 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {
let tempFilter = [];
const tempFilter = [];
let tempSortDir = '';
if (status) {
tempFilter.push(`status = "${status}"`);
if (state) {
tempFilter.push(`state = "${state}"`);
}
if (sortDir) {
@@ -130,7 +130,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
preFinalListOption = { ...preFinalListOption, sort: tempSortDir };
}
setListOption(preFinalListOption);
}, [sortDir, email, phone, status]);
}, [sortDir, email, phone, state]);
if (showLoading) return <FormLoading />;
@@ -178,7 +178,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
<TeachersSelectionProvider teachers={f}>
<Card>
<TeachersFilters
filters={{ email, phone, status }}
filters={{ email, phone, state }}
fullData={teacherData}
sortDir={sortDir}
/>
@@ -212,7 +212,7 @@ interface PageProps {
email?: string;
phone?: string;
sortDir?: 'asc' | 'desc';
status?: string;
state?: string;
//
};
}

View File

@@ -1,216 +0,0 @@
// src/app/dashboard/teachers/list/page.tsx
'use client';
// RULES:
// contains list page for teachers (Teachers)
//
import * as React from 'react';
import { useRouter } from 'next/navigation';
import { COL_USER_METAS } from '@/constants';
import { LoadingButton } from '@mui/lab';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import Divider from '@mui/material/Divider';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
import type { ListResult, RecordModel } from 'pocketbase';
import { TeachersFilters } from '@/components/dashboard/teacher/teachers-filters';
import { TeachersPagination } from '@/components/dashboard/teacher/teachers-pagination';
import { TeachersSelectionProvider } from '@/components/dashboard/teacher/teachers-selection-context';
import { TeachersTable } from '@/components/dashboard/teacher/teachers-table';
import type { Teacher } from '@/components/dashboard/teacher/type.d';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import isDevelopment from '@/lib/check-is-development';
import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import ErrorDisplay from '@/components/dashboard/error';
import { defaultTeacher } from '@/components/dashboard/teacher/_constants';
import FormLoading from '@/components/loading';
export default function Page({ searchParams }: PageProps): React.JSX.Element {
const { t } = useTranslation(['teachers']);
const router = useRouter();
const { email, phone, sortDir, status } = searchParams;
const [teacherData, setTeacherData] = React.useState<Teacher[]>([]);
const [isLoadingAddPage, setIsLoadingAddPage] = React.useState<boolean>(false);
const [showLoading, setShowLoading] = React.useState<boolean>(true);
const [showError, setShowError] = React.useState({ show: false, detail: '' });
//
const [rowsPerPage, setRowsPerPage] = React.useState<number>(5);
//
const [f, setF] = React.useState<Teacher[]>([]);
const [currentPage, setCurrentPage] = React.useState<number>(0);
//
const [recordCount, setRecordCount] = React.useState<number>(0);
const [listOption, setListOption] = React.useState({ filter: '' });
const [listSort, setListSort] = React.useState({});
function isListOptionChanged() {
return JSON.stringify(listOption) === '{}';
}
//
const reloadRows = async (): Promise<void> => {
try {
const listOptionTeacherOnly = isListOptionChanged()
? { filter: `role = "teacher"` }
: { filter: [listOption.filter, `role = "teacher"`].join(' && ') };
const models: ListResult<RecordModel> = await pb
.collection(COL_USER_METAS)
.getList(currentPage + 1, rowsPerPage, listOptionTeacherOnly);
const { items, totalItems } = models;
const tempTeacher: Teacher[] = items.map((lt) => {
return { ...defaultTeacher, ...lt };
});
setTeacherData(tempTeacher);
setRecordCount(totalItems);
setF(tempTeacher);
} catch (error) {
logger.error(error);
setShowError({
//
show: true,
detail: JSON.stringify(error, null, 2),
});
} finally {
setShowLoading(false);
}
};
const [lastListOption, setLastListOption] = React.useState({});
const isFirstRun = React.useRef(false);
React.useEffect(() => {
if (!isFirstRun.current) {
isFirstRun.current = true;
} else if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
// reset page number as tab changes
setLastListOption(listOption);
setCurrentPage(0);
void reloadRows();
} else {
void reloadRows();
}
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {
const tempFilter = [];
let tempSortDir = '';
if (status) {
tempFilter.push(`status = "${status}"`);
}
if (sortDir) {
tempSortDir = `-created`;
}
if (email) {
tempFilter.push(`email ~ "%${email}%"`);
}
if (phone) {
tempFilter.push(`phone ~ "%${phone}%"`);
}
let preFinalListOption = { filter: '' };
if (tempFilter.length > 0) {
preFinalListOption = { filter: tempFilter.join(' && ') };
}
if (tempSortDir.length > 0) {
preFinalListOption = { ...preFinalListOption, sort: tempSortDir };
}
setListOption(preFinalListOption);
}, [sortDir, email, phone, status]);
if (showLoading) return <FormLoading />;
if (showError.show)
return (
<ErrorDisplay
message={t('error.unable-to-process-request')}
code={-1}
details={showError.detail}
/>
);
return (
<Box
sx={{
maxWidth: 'var(--Content-maxWidth)',
m: 'var(--Content-margin)',
p: 'var(--Content-padding)',
width: 'var(--Content-width)',
}}
>
<Stack spacing={4}>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={3}
sx={{ alignItems: 'flex-start' }}
>
<Box sx={{ flex: '1 1 auto' }}>
<Typography variant="h4">{t('list.title')}</Typography>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<LoadingButton
loading={isLoadingAddPage}
onClick={(): void => {
setIsLoadingAddPage(true);
router.push(paths.dashboard.teachers.create);
}}
startIcon={<PlusIcon />}
variant="contained"
>
{t('list.add')}
</LoadingButton>
</Box>
</Stack>
<TeachersSelectionProvider teachers={f}>
<Card>
<TeachersFilters
filters={{ email, phone, status }}
fullData={teacherData}
sortDir={sortDir}
/>
<Divider />
<Box sx={{ overflowX: 'auto' }}>
<TeachersTable
reloadRows={reloadRows}
rows={f}
/>
</Box>
<Divider />
<TeachersPagination
count={recordCount}
page={currentPage}
rowsPerPage={rowsPerPage}
setPage={setCurrentPage}
setRowsPerPage={setRowsPerPage}
/>
</Card>
</TeachersSelectionProvider>
</Stack>
<Box sx={{ display: isDevelopment ? 'block' : 'none' }}>
<pre>{JSON.stringify(f, null, 2)}</pre>
</Box>
</Box>
);
}
interface PageProps {
searchParams: {
email?: string;
phone?: string;
sortDir?: 'asc' | 'desc';
status?: string;
//
};
}

View File

@@ -9,13 +9,9 @@ import Typography from '@mui/material/Typography';
import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown';
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import { Teacher } from '@/components/dashboard/teacher/type.d';
// import type { CrCategory } from '@/components/dashboard/cr/categories/type';
function getImageUrlFrRecord(teacher: Teacher): string {
return `http://127.0.0.1:8090/api/files/${teacher.collectionId}/${teacher.id}/${teacher.avatar}`;
}
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { Teacher } from '@/components/dashboard/teacher/type.d';
export default function SampleTitleCard({ teacherRecord }: { teacherRecord: Teacher }): React.JSX.Element {
const { t } = useTranslation();
@@ -29,7 +25,7 @@ export default function SampleTitleCard({ teacherRecord }: { teacherRecord: Teac
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(teacherRecord)}
src={getImageUrlFromFile(teacherRecord.collectionId, teacherRecord.id, teacherRecord.avatar)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -1,3 +0,0 @@
this `tsx` file is clone from elsewhere, please understand, modify and update the content of `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/app/dashboard/user_metas/edit/[id]/page.tsx.draft` to handle `UserMeta` record thanks, modify comments/variables/paths/functions name please
e.g. why `lessonCategories` still exist ?

View File

@@ -1,8 +1,9 @@
'use client';
// src/app/dashboard/user_metas/edit/[id]/page.tsx
// src/app/dashboard/user_metas/edit/[id]/page.tsx
import * as React from 'react';
import RouterLink from 'next/link';
import { useParams } from 'next/navigation';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
@@ -11,11 +12,12 @@ import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/Arrow
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { CrCategoryEditForm } from '@/components/dashboard/cr/categories/cr-category-edit-form';
import { UserActivationEditForm } from '@/components/dashboard/user_meta/user-activation-edit-form';
import { UserMetaEditForm } from '@/components/dashboard/user_meta/user-meta-edit-form';
export default function Page(): React.JSX.Element {
const { t } = useTranslation(['lp_categories']);
const { t } = useTranslation(['user_metas']);
const { id: userId } = useParams<{ id: string }>();
React.useEffect(() => {
// console.log('helloworld');
@@ -49,6 +51,7 @@ export default function Page(): React.JSX.Element {
</div>
</Stack>
<UserMetaEditForm />
<UserActivationEditForm userId={userId} />
</Stack>
</Box>
);

View File

@@ -15,12 +15,6 @@ import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
import type { ListResult, RecordModel } from 'pocketbase';
import { UserMetasFilters } from '@/components/dashboard/user_meta/user-metas-filters';
import { UserMetasPagination } from '@/components/dashboard/user_meta/user-metas-pagination';
import { UserMetasSelectionProvider } from '@/components/dashboard/user_meta/user-metas-selection-context';
import { UserMetasTable } from '@/components/dashboard/user_meta/user-metas-table';
import type { UserMeta } from '@/components/dashboard/user_meta/type.d';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
@@ -29,13 +23,18 @@ import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import ErrorDisplay from '@/components/dashboard/error';
import { defaultUserMeta } from '@/components/dashboard/user_meta/_constants';
import type { UserMeta } from '@/components/dashboard/user_meta/type.d';
import { UserMetasFilters } from '@/components/dashboard/user_meta/user-metas-filters';
import { UserMetasPagination } from '@/components/dashboard/user_meta/user-metas-pagination';
import { UserMetasSelectionProvider } from '@/components/dashboard/user_meta/user-metas-selection-context';
import { UserMetasTable } from '@/components/dashboard/user_meta/user-metas-table';
import FormLoading from '@/components/loading';
export default function Page({ searchParams }: PageProps): React.JSX.Element {
const { t } = useTranslation(['teachers']);
const { t } = useTranslation(['user_metas']);
const router = useRouter();
const { email, phone, sortDir, status } = searchParams;
const { email, phone, sortDir, state } = searchParams;
const [userMetaData, setUserMetaData] = React.useState<UserMeta[]>([]);
@@ -97,11 +96,11 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
}, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => {
let tempFilter = [];
const tempFilter = [];
let tempSortDir = '';
if (status) {
tempFilter.push(`status = "${status}"`);
if (state) {
tempFilter.push(`state = "${state}"`);
}
if (sortDir) {
@@ -124,7 +123,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
preFinalListOption = { ...preFinalListOption, sort: tempSortDir };
}
setListOption(preFinalListOption);
}, [sortDir, email, phone, status]);
}, [sortDir, email, phone, state]);
if (showLoading) return <FormLoading />;
@@ -172,7 +171,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
<UserMetasSelectionProvider userMetas={f}>
<Card>
<UserMetasFilters
filters={{ email, phone, status }}
filters={{ email, phone, state }}
fullData={userMetaData}
sortDir={sortDir}
/>
@@ -206,7 +205,7 @@ interface PageProps {
email?: string;
phone?: string;
sortDir?: 'asc' | 'desc';
status?: string;
state?: string;
//
};
}

View File

@@ -14,7 +14,7 @@ import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
// import { CrCategory } from '@/components/dashboard/cr/categories/type';
import type { UserMeta } from '@/components/dashboard/user_meta/type.d';
import type { UserMeta } from '@/components/dashboard/user_meta/type_move.d';
export default function BasicDetailCard({
userMeta,

View File

@@ -9,13 +9,9 @@ import Typography from '@mui/material/Typography';
import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown';
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import { Teacher } from '@/components/dashboard/teacher/type.d';
// import type { CrCategory } from '@/components/dashboard/cr/categories/type';
function getImageUrlFrRecord(teacher: Teacher): string {
return `http://127.0.0.1:8090/api/files/${teacher.collectionId}/${teacher.id}/${teacher.avatar}`;
}
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import type { Teacher } from '@/components/dashboard/teacher/type.d';
export default function SampleTitleCard({ teacherRecord }: { teacherRecord: Teacher }): React.JSX.Element {
const { t } = useTranslation();
@@ -29,7 +25,7 @@ export default function SampleTitleCard({ teacherRecord }: { teacherRecord: Teac
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(teacherRecord)}
src={getImageUrlFromFile(teacherRecord.collectionId, teacherRecord.id, teacherRecord.avatar)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}

View File

@@ -1,5 +1,9 @@
'use client';
// src/app/dashboard/user_metas/view/[id]/page.tsx
// PURPOSE
// T.B.A.
//
import * as React from 'react';
import RouterLink from 'next/link';
import { useParams, useRouter } from 'next/navigation';
@@ -7,7 +11,7 @@ import SampleAddressCard from '@/app/dashboard/Sample/AddressCard';
import { SampleNotifications } from '@/app/dashboard/Sample/Notifications';
import SamplePaymentCard from '@/app/dashboard/Sample/PaymentCard';
import SampleSecurityCard from '@/app/dashboard/Sample/SecurityCard';
import { COL_USER_METAS } from '@/constants';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
@@ -21,16 +25,14 @@ import { paths } from '@/paths';
import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import { toast } from '@/components/core/toaster';
import ErrorDisplay from '@/components/dashboard/error';
import { defaultUserMeta } from '@/components/dashboard/user_meta/_constants';
import { Notifications } from '@/components/dashboard/user_meta/notifications';
import type { UserMeta } from '@/components/dashboard/user_meta/type_move.d';
import FormLoading from '@/components/loading';
import BasicDetailCard from './BasicDetailCard';
import TitleCard from './TitleCard';
import { defaultUserMeta } from '@/components/dashboard/user_meta/_constants';
import type { UserMeta } from '@/components/dashboard/user_meta/type.d';
import { COL_USER_METAS } from '@/constants';
export default function Page(): React.JSX.Element {
const { t } = useTranslation();

View File

@@ -1,5 +1,5 @@
import { dayjs } from '@/lib/dayjs';
import { LessonCategory } from '@/components/dashboard/lesson_category/type';
import type { LessonCategory } from '@/components/dashboard/lesson_category/type';
// import type { LessonCategory } from '@/components/dashboard/lp_categories/type';

View File

@@ -34,6 +34,7 @@ import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { dayjs } from '@/lib/dayjs';
import { logger } from '@/lib/default-logger';
import getImageUrlFromFile from '@/lib/get-image-url-from-file.ts';
import { pb } from '@/lib/pb';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
@@ -128,7 +129,11 @@ export default function Page(): React.JSX.Element {
sx={{ alignItems: 'center', flex: '1 1 auto' }}
>
<Avatar
src={`http://127.0.0.1:8090/api/files/${showLessonCategory.collectionId}/${showLessonCategory.id}/${showLessonCategory.cat_image}`}
src={getImageUrlFromFile(
showLessonCategory.collectionId,
showLessonCategory.id,
showLessonCategory.image
)}
sx={{ '--Avatar-size': '64px' }}
variant="rounded"
>

View File

@@ -0,0 +1,5 @@
export interface OAuthProvider {
id: 'google' | 'discord' | 'github';
name: string;
logo: string;
}

View File

@@ -0,0 +1,14 @@
'use client';
import type { OAuthProvider } from './OAuthProvider';
// export const oAuthProviders = [
// { id: 'google', name: 'Google', logo: '/assets/logo-google.svg' },
// { id: 'discord', name: 'Discord', logo: '/assets/logo-discord.svg' },
// ] satisfies OAuthProvider[];
export const oAuthProviders = [
{ id: 'google', name: 'Google', logo: '/assets/logo-google.svg' },
{ id: 'github', name: 'Github', logo: '/assets/logo-github.svg' },
// { id: 'discord', name: 'Discord', logo: '/assets/logo-discord.svg' },
] satisfies OAuthProvider[];

View File

@@ -1,10 +1,9 @@
'use client';
import * as React from 'react';
import RouterLink from 'next/link';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import Alert from '@mui/material/Alert';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import FormControl from '@mui/material/FormControl';
import FormHelperText from '@mui/material/FormHelperText';
@@ -13,21 +12,28 @@ import OutlinedInput from '@mui/material/OutlinedInput';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z as zod } from 'zod';
import { paths } from '@/paths';
import { authClient } from '@/lib/auth/custom/client';
import { DynamicLogo } from '@/components/core/logo';
const schema = zod.object({ email: zod.string().min(1, { message: 'Email is required' }).email() });
type Values = zod.infer<typeof schema>;
const defaultValues = { email: '' } satisfies Values;
export function ResetPasswordForm(): React.JSX.Element {
const { t } = useTranslation(['sign_in']);
const router = useRouter();
const [isPending, setIsPending] = React.useState<boolean>(false);
const schema = zod.object({
email: zod
.string()
.min(1, { message: t('email-is-required') })
.email(),
});
type Values = zod.infer<typeof schema>;
const defaultValues = { email: '' } satisfies Values;
const {
control,
handleSubmit,
@@ -54,14 +60,29 @@ export function ResetPasswordForm(): React.JSX.Element {
[setError]
);
function handleBackToLoginClick(): void {
router.replace(paths.auth.custom.signIn);
}
return (
<Stack spacing={4}>
<div>
<Box component={RouterLink} href={paths.home} sx={{ display: 'inline-block', fontSize: 0 }}>
<DynamicLogo colorDark="light" colorLight="dark" height={32} width={122} />
{/*
<div>
<Box
component={RouterLink}
href={paths.home}
sx={{ display: 'inline-block', fontSize: 0 }}
>
<DynamicLogo
colorDark="light"
colorLight="dark"
height={32}
width={122}
/>
</Box>
</div>
<Typography variant="h5">Reset password</Typography>
*/}
<Typography variant="h5">{t('reset-password')}</Typography>
<form onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={2}>
<Controller
@@ -69,16 +90,39 @@ export function ResetPasswordForm(): React.JSX.Element {
name="email"
render={({ field }) => (
<FormControl error={Boolean(errors.email)}>
<InputLabel>Email address</InputLabel>
<OutlinedInput {...field} type="email" />
<InputLabel>{t('email-address')}</InputLabel>
<OutlinedInput
{...field}
type="email"
/>
{errors.email ? <FormHelperText>{errors.email.message}</FormHelperText> : null}
</FormControl>
)}
/>
{errors.root ? <Alert color="error">{errors.root.message}</Alert> : null}
<Button disabled={isPending} type="submit" variant="contained">
Send recovery link
</Button>
<Stack
direction="row"
spacing={4}
justifyContent="space-between"
>
<Button
disabled={isPending}
type="reset"
variant="outlined"
color="secondary"
onClick={handleBackToLoginClick}
>
{t('back-to-login')}
</Button>
<Button
disabled={isPending}
type="submit"
variant="contained"
>
{t('send-recovery-link')}
</Button>
</Stack>
</Stack>
</form>
</Stack>

View File

@@ -1,11 +1,13 @@
'use client';
// RULES:
// refer to ticket REQ0016 for login flow
//
import * as React from 'react';
import RouterLink from 'next/link';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { LoadingButton } from '@mui/lab';
import Alert from '@mui/material/Alert';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
@@ -20,6 +22,7 @@ import Typography from '@mui/material/Typography';
import { Eye as EyeIcon } from '@phosphor-icons/react/dist/ssr/Eye';
import { EyeSlash as EyeSlashIcon } from '@phosphor-icons/react/dist/ssr/EyeSlash';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z as zod } from 'zod';
import { paths } from '@/paths';
@@ -27,30 +30,25 @@ import { authClient } from '@/lib/auth/custom/client';
import { useUser } from '@/hooks/use-user';
import { DynamicLogo } from '@/components/core/logo';
import { toast } from '@/components/core/toaster';
import { pb } from '@/lib/pb';
interface OAuthProvider {
id: 'google' | 'discord';
name: string;
logo: string;
}
import type { OAuthProvider } from '../OAuthProvider';
import { oAuthProviders } from '../oAuthProviders';
const oAuthProviders = [
{ id: 'google', name: 'Google', logo: '/assets/logo-google.svg' },
{ id: 'discord', name: 'Discord', logo: '/assets/logo-discord.svg' },
] satisfies OAuthProvider[];
// interface OAuthProvider {
// id: 'google' | 'discord' | 'github';
// name: string;
// logo: string;
// }
const schema = zod.object({
email: zod.string().min(1, { message: 'Email is required' }).email(),
password: zod.string().min(1, { message: 'Password is required' }),
});
type Values = zod.infer<typeof schema>;
const defaultValues = { email: 'admin@123.com', password: 'admin@123.com' } satisfies Values;
// const oAuthProviders = [
// { id: 'google', name: 'Google', logo: '/assets/logo-google.svg' },
// { id: 'github', name: 'Github', logo: '/assets/logo-github.svg' },
// // { id: 'discord', name: 'Discord', logo: '/assets/logo-discord.svg' },
// ] satisfies OAuthProvider[];
export function SignInForm(): React.JSX.Element {
const router = useRouter();
const { t } = useTranslation(['sign_in']);
const { checkSession } = useUser();
@@ -58,6 +56,14 @@ export function SignInForm(): React.JSX.Element {
const [isPending, setIsPending] = React.useState<boolean>(false);
const schema = zod.object({
email: zod.string().min(1, { message: 'Email is required' }).email(),
password: zod.string().min(1, { message: 'Password is required' }),
});
type Values = zod.infer<typeof schema>;
const defaultValues = { email: '', password: '' } satisfies Values;
const {
control,
handleSubmit,
@@ -98,14 +104,16 @@ export function SignInForm(): React.JSX.Element {
// UserProvider, for this case, will not refresh the router
// After refresh, GuestGuard will handle the redirect
router.refresh();
// TODO: resume me
// router.refresh();
},
[checkSession, router, setError]
);
return (
<Stack spacing={4}>
<div>
<Box sx={{ display: { xs: 'inline-block', sm: 'none' } }}>
<Box
component={RouterLink}
href={paths.home}
@@ -118,31 +126,34 @@ export function SignInForm(): React.JSX.Element {
width={122}
/>
</Box>
</div>
</Box>
<Stack spacing={1}>
<Typography variant="h5">Sign in</Typography>
<Typography variant="h5">{t('sign-in')}</Typography>
<Typography
color="text.secondary"
variant="body2"
>
Don&apos;t have an account?{' '}
{t('dont-have-an-account')}{' '}
<Link
component={RouterLink}
href={paths.auth.custom.signUp}
variant="subtitle2"
>
Sign up
{t('sign-up')}
</Link>
</Typography>
</Stack>
<Stack spacing={3}>
<Stack spacing={2}>
<Stack
spacing={2}
direction="row"
>
{oAuthProviders.map(
(provider): React.JSX.Element => (
<Button
color="secondary"
disabled={isPending}
endIcon={
startIcon={
<Box
alt=""
component="img"
@@ -159,12 +170,12 @@ export function SignInForm(): React.JSX.Element {
}}
variant="outlined"
>
Continue with {provider.name}
{t('continue-with_fh')} {provider.name} {t('continue-with_sh')}
</Button>
)
)}
</Stack>
<Divider>or</Divider>
<Divider>{t('or')}</Divider>
<Stack spacing={2}>
<form onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={2}>
@@ -173,10 +184,12 @@ export function SignInForm(): React.JSX.Element {
name="email"
render={({ field }) => (
<FormControl error={Boolean(errors.email)}>
<InputLabel>Email address</InputLabel>
<InputLabel>{t('email-address')}</InputLabel>
<OutlinedInput
{...field}
placeholder="name@example.com"
type="email"
disabled={isPending}
/>
{errors.email ? <FormHelperText>{errors.email.message}</FormHelperText> : null}
</FormControl>
@@ -187,9 +200,10 @@ export function SignInForm(): React.JSX.Element {
name="password"
render={({ field }) => (
<FormControl error={Boolean(errors.password)}>
<InputLabel>Password</InputLabel>
<InputLabel>{t('password')}</InputLabel>
<OutlinedInput
{...field}
placeholder={t('password')}
endAdornment={
showPassword ? (
<EyeIcon
@@ -211,19 +225,22 @@ export function SignInForm(): React.JSX.Element {
}
label="Password"
type={showPassword ? 'text' : 'password'}
disabled={isPending}
/>
{errors.password ? <FormHelperText>{errors.password.message}</FormHelperText> : null}
</FormControl>
)}
/>
{errors.root ? <Alert color="error">{errors.root.message}</Alert> : null}
<Button
<LoadingButton
loading={isPending}
disabled={isPending}
type="submit"
variant="contained"
>
Sign in
</Button>
{t('sign-in')}
</LoadingButton>
</Stack>
</form>
<div>
@@ -232,7 +249,7 @@ export function SignInForm(): React.JSX.Element {
href={paths.auth.custom.resetPassword}
variant="subtitle2"
>
Forgot password?
{t('forgot-password')} ?
</Link>
</div>
</Stack>
@@ -240,7 +257,7 @@ export function SignInForm(): React.JSX.Element {
<Alert color="warning">
<Stack>
<Box>
user:{' '}
{t('user')}:{' '}
<Typography
component="span"
sx={{ fontWeight: 700 }}
@@ -248,7 +265,7 @@ export function SignInForm(): React.JSX.Element {
>
admin@123.com
</Typography>{' '}
password{' '}
{t('password')}{' '}
<Typography
component="span"
sx={{ fontWeight: 700 }}
@@ -258,7 +275,7 @@ export function SignInForm(): React.JSX.Element {
</Typography>
</Box>
<Box>
user{' '}
{t('user')}{' '}
<Typography
component="span"
sx={{ fontWeight: 700 }}
@@ -266,7 +283,7 @@ export function SignInForm(): React.JSX.Element {
>
sofia@devias.io
</Typography>{' '}
password{' '}
{t('password')}{' '}
<Typography
component="span"
sx={{ fontWeight: 700 }}

View File

@@ -1,9 +1,12 @@
'use client';
// RULES:
// refer to ticket REQ0016 for login flow
import * as React from 'react';
import RouterLink from 'next/link';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { LoadingButton } from '@mui/lab';
import Alert from '@mui/material/Alert';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
@@ -18,6 +21,7 @@ import OutlinedInput from '@mui/material/OutlinedInput';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z as zod } from 'zod';
import { paths } from '@/paths';
@@ -26,36 +30,30 @@ import { useUser } from '@/hooks/use-user';
import { DynamicLogo } from '@/components/core/logo';
import { toast } from '@/components/core/toaster';
interface OAuthProvider {
id: 'google' | 'discord';
name: string;
logo: string;
}
const oAuthProviders = [
{ id: 'google', name: 'Google', logo: '/assets/logo-google.svg' },
{ id: 'discord', name: 'Discord', logo: '/assets/logo-discord.svg' },
] satisfies OAuthProvider[];
const schema = zod.object({
firstName: zod.string().min(1, { message: 'First name is required' }),
lastName: zod.string().min(1, { message: 'Last name is required' }),
email: zod.string().min(1, { message: 'Email is required' }).email(),
password: zod.string().min(6, { message: 'Password should be at least 6 characters' }),
terms: zod.boolean().refine((value) => value, 'You must accept the terms and conditions'),
});
type Values = zod.infer<typeof schema>;
const defaultValues = { firstName: '', lastName: '', email: '', password: '', terms: false } satisfies Values;
import type { OAuthProvider } from '../OAuthProvider';
import { oAuthProviders } from '../oAuthProviders';
export function SignUpForm(): React.JSX.Element {
const router = useRouter();
const { t } = useTranslation(['sign_in']);
const { checkSession } = useUser();
const [isPending, setIsPending] = React.useState<boolean>(false);
const schema = zod.object({
firstName: zod.string().min(1, { message: t('first-name-is-required') }),
lastName: zod.string().min(1, { message: t('last-name-is-required') }),
email: zod
.string()
.min(1, { message: t('email-is-required') })
.email(),
password: zod.string().min(6, { message: t('password-should-be-at-least-6-characters') }),
terms: zod.boolean().refine((value) => value, t('you-must-accept-the-terms-and-conditions')),
});
type Values = zod.infer<typeof schema>;
const defaultValues = { firstName: '', lastName: '', email: '', password: '', terms: false } satisfies Values;
const {
control,
handleSubmit,
@@ -96,7 +94,9 @@ export function SignUpForm(): React.JSX.Element {
// UserProvider, for this case, will not refresh the router
// After refresh, GuestGuard will handle the redirect
router.refresh();
// TODO: resume me
// router.refresh();
},
[checkSession, router, setError]
);
@@ -104,16 +104,32 @@ export function SignUpForm(): React.JSX.Element {
return (
<Stack spacing={4}>
<div>
<Box component={RouterLink} href={paths.home} sx={{ display: 'inline-block', fontSize: 0 }}>
<DynamicLogo colorDark="light" colorLight="dark" height={32} width={122} />
<Box
component={RouterLink}
href={paths.home}
sx={{ display: 'inline-block', fontSize: 0 }}
>
<DynamicLogo
colorDark="light"
colorLight="dark"
height={32}
width={122}
/>
</Box>
</div>
<Stack spacing={1}>
<Typography variant="h5">Sign up</Typography>
<Typography color="text.secondary" variant="body2">
Already have an account?{' '}
<Link component={RouterLink} href={paths.auth.custom.signIn} variant="subtitle2">
Sign in
<Typography variant="h5">{t('sign-up')}</Typography>
<Typography
color="text.secondary"
variant="body2"
>
{t('already-have-an-account')}{' '}
<Link
component={RouterLink}
href={paths.auth.custom.signIn}
variant="subtitle2"
>
{t('sign-in')}
</Link>
</Typography>
</Stack>
@@ -124,7 +140,15 @@ export function SignUpForm(): React.JSX.Element {
<Button
color="secondary"
disabled={isPending}
endIcon={<Box alt="" component="img" height={24} src={provider.logo} width={24} />}
startIcon={
<Box
alt=""
component="img"
height={24}
src={provider.logo}
width={24}
/>
}
key={provider.id}
onClick={(): void => {
onAuth(provider.id).catch(() => {
@@ -133,12 +157,12 @@ export function SignUpForm(): React.JSX.Element {
}}
variant="outlined"
>
Continue with {provider.name}
{t('continue-with_fh')} {provider.name} {t('continue-with_sh')}
</Button>
)
)}
</Stack>
<Divider>or</Divider>
<Divider>{t('or')}</Divider>
<form onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={2}>
<Controller
@@ -146,7 +170,7 @@ export function SignUpForm(): React.JSX.Element {
name="firstName"
render={({ field }) => (
<FormControl error={Boolean(errors.firstName)}>
<InputLabel>First name</InputLabel>
<InputLabel>{t('first-name')}:</InputLabel>
<OutlinedInput {...field} />
{errors.firstName ? <FormHelperText>{errors.firstName.message}</FormHelperText> : null}
</FormControl>
@@ -157,7 +181,7 @@ export function SignUpForm(): React.JSX.Element {
name="lastName"
render={({ field }) => (
<FormControl error={Boolean(errors.lastName)}>
<InputLabel>Last name</InputLabel>
<InputLabel>{t('last-name')}:</InputLabel>
<OutlinedInput {...field} />
{errors.lastName ? <FormHelperText>{errors.lastName.message}</FormHelperText> : null}
</FormControl>
@@ -168,8 +192,13 @@ export function SignUpForm(): React.JSX.Element {
name="email"
render={({ field }) => (
<FormControl error={Boolean(errors.email)}>
<InputLabel>Email address</InputLabel>
<OutlinedInput {...field} type="email" />
<InputLabel>{t('email-address')}:</InputLabel>
<OutlinedInput
{...field}
placeholder={`${t('e.g.') } name@example.com`}
type="email"
disabled={isPending}
/>
{errors.email ? <FormHelperText>{errors.email.message}</FormHelperText> : null}
</FormControl>
)}
@@ -179,8 +208,12 @@ export function SignUpForm(): React.JSX.Element {
name="password"
render={({ field }) => (
<FormControl error={Boolean(errors.password)}>
<InputLabel>Password</InputLabel>
<OutlinedInput {...field} type="password" />
<InputLabel>{t('password')}:</InputLabel>
<OutlinedInput
{...field}
placeholder={t('password')}
type="password"
/>
{errors.password ? <FormHelperText>{errors.password.message}</FormHelperText> : null}
</FormControl>
)}
@@ -194,7 +227,7 @@ export function SignUpForm(): React.JSX.Element {
control={<Checkbox {...field} />}
label={
<React.Fragment>
I have read the <Link>terms and conditions</Link>
{t('i-have-read-the')} <Link>{t('terms-and-conditions')}</Link>
</React.Fragment>
}
/>
@@ -203,13 +236,19 @@ export function SignUpForm(): React.JSX.Element {
)}
/>
{errors.root ? <Alert color="error">{errors.root.message}</Alert> : null}
<Button disabled={isPending} type="submit" variant="contained">
Create account
</Button>
<LoadingButton
loading={isPending}
disabled={isPending}
type="submit"
variant="contained"
>
{t('create-account')}
</LoadingButton>
</Stack>
</form>
</Stack>
<Alert color="warning">Created users are not persisted</Alert>
<Alert color="warning">{t('created-users-are-not-persisted')}</Alert>
</Stack>
);
}

View File

@@ -1,123 +0,0 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
export interface SplitLayoutProps {
children: React.ReactNode;
}
export function SplitLayout({ children }: SplitLayoutProps): React.JSX.Element {
return (
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', lg: '1fr 800px' }, minHeight: '100vh' }}>
<Box
sx={{
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'var(--mui-palette-background-level1)',
display: { xs: 'none', lg: 'flex' },
flexDirection: 'column',
p: 3,
}}
>
<Stack spacing={4} sx={{ maxWidth: '700px' }}>
<Stack spacing={1}>
<Typography variant="h4">Welcome to Devias Kit PRO</Typography>
<Typography color="text.secondary">
A professional template that comes with ready-to-use MUI components developed with one common goal in
mind, help you build faster & beautiful applications.
</Typography>
</Stack>
<Stack
direction="row"
spacing={3}
sx={{ alignItems: 'center', color: 'var(--mui-palette-neutral-500)', flexWrap: 'wrap' }}
>
<svg fill="none" height="36" viewBox="0 0 133 36" width="133" xmlns="http://www.w3.org/2000/svg">
<path
d="M77.4069 9.92864L85.0581 7.0798L77.4069 4.14957V0.161194L90.5116 5.45189V8.70771L77.4069 13.9984V9.92864Z"
fill="currentColor"
/>
<path
d="M5.04645 35.1612C2.27901 35.1612 -6.10352e-05 33.7775 -6.10352e-05 30.6844V30.5216C-6.10352e-05 26.7775 3.25575 25.4751 7.24413 25.4751H9.11622V24.7426C9.11622 23.1961 8.46506 22.3007 6.83715 22.3007C5.37203 22.3007 4.63947 23.1147 4.55808 24.2542H0.488311C0.813893 20.8356 3.49994 19.2077 7.08134 19.2077C10.7441 19.2077 13.4302 20.7542 13.4302 24.5798V34.8356H9.27901V33.0449C8.46506 34.1844 7.08134 35.1612 5.04645 35.1612ZM9.11622 29.7891V28.324H7.40692C5.29064 28.324 4.2325 28.8937 4.2325 30.2775V30.4403C4.2325 31.4984 4.88366 32.231 6.34878 32.231C7.81389 32.1496 9.11622 31.3356 9.11622 29.7891ZM23.1162 35.1612C18.8837 35.1612 15.7906 32.5565 15.7906 27.3472V27.103C15.7906 21.8937 19.0465 19.1263 23.1162 19.1263C26.6162 19.1263 29.4651 20.917 29.7906 24.9054H25.7209C25.4767 23.4403 24.6627 22.4635 23.1976 22.4635C21.4069 22.4635 20.1046 23.9286 20.1046 26.9403V27.4286C20.1046 30.5216 21.2441 31.9054 23.1976 31.9054C24.6627 31.9054 25.7209 30.8472 25.9651 29.1379H29.872C29.6279 32.7193 27.2674 35.1612 23.1162 35.1612ZM39.0697 35.1612C34.8372 35.1612 31.7441 32.5565 31.7441 27.3472V27.103C31.7441 21.8937 34.9999 19.1263 39.0697 19.1263C42.5697 19.1263 45.4186 20.917 45.7441 24.9054H41.6744C41.4302 23.4403 40.6162 22.4635 39.1511 22.4635C37.3604 22.4635 36.0581 23.9286 36.0581 26.9403V27.4286C36.0581 30.5216 37.1976 31.9054 39.1511 31.9054C40.6162 31.9054 41.6744 30.8472 41.9186 29.1379H45.8255C45.5813 32.7193 43.2209 35.1612 39.0697 35.1612ZM55.1046 35.1612C50.7093 35.1612 47.6976 32.5565 47.6976 27.4286V27.103C47.6976 21.9751 50.872 19.1263 55.0232 19.1263C58.8488 19.1263 62.0232 21.2426 62.0232 26.3705V28.2426H52.0116C52.1744 31.01 53.3953 32.0682 55.186 32.0682C56.8139 32.0682 57.7093 31.1728 58.0348 30.1147H62.0232C61.5348 32.9635 59.093 35.1612 55.1046 35.1612ZM52.093 25.3937H57.7907C57.7093 23.1147 56.6511 22.1379 54.9418 22.1379C53.6395 22.2193 52.4186 22.9519 52.093 25.3937ZM64.6279 19.5333H68.9418V21.8123C69.6744 20.3472 71.2209 19.2077 73.5814 19.2077C76.3488 19.2077 78.2209 20.917 78.2209 24.5798V34.8356H73.9069V25.2309C73.9069 23.4403 73.1744 22.6263 71.6279 22.6263C70.1627 22.6263 68.9418 23.5216 68.9418 25.4751V34.8356H64.6279V19.5333ZM86.1162 14.8937V19.5333H89.0465V22.7077H86.1162V29.9519C86.1162 31.0914 86.6046 31.6612 87.6627 31.6612C88.3139 31.6612 88.7209 31.5798 89.1279 31.417V34.7542C88.6395 34.917 87.7441 35.0798 86.686 35.0798C83.3488 35.0798 81.8023 33.5333 81.8023 30.4403V22.7077H80.0116V19.5333H81.8023V16.6844L86.1162 14.8937ZM105.163 34.8356H100.93V32.5565C100.198 34.0216 98.7325 35.1612 96.4535 35.1612C93.686 35.1612 91.6511 33.4519 91.6511 29.8705V19.5333H95.9651V29.3007C95.9651 31.0914 96.6976 31.9054 98.1628 31.9054C99.6279 31.9054 100.849 30.9286 100.849 29.0565V19.5333H105.163V34.8356ZM108.337 19.5333H112.651V22.3821C113.546 20.3472 115.012 19.3705 117.291 19.3705V23.603C114.36 23.603 112.651 24.4984 112.651 27.0216V34.917H108.337V19.5333ZM126 35.1612C121.605 35.1612 118.593 32.5565 118.593 27.4286V27.103C118.593 21.9751 121.767 19.1263 125.919 19.1263C129.744 19.1263 132.919 21.2426 132.919 26.3705V28.2426H122.988C123.151 31.01 124.372 32.0682 126.163 32.0682C127.791 32.0682 128.686 31.1728 129.012 30.1147H133C132.349 32.9635 129.988 35.1612 126 35.1612ZM122.907 25.3937H128.686C128.605 23.1147 127.546 22.1379 125.837 22.1379C124.535 22.2193 123.314 22.9519 122.907 25.3937Z"
fill="currentColor"
/>
</svg>
<svg fill="none" height="33" viewBox="0 0 78 33" width="78" xmlns="http://www.w3.org/2000/svg">
<path
d="M72.216 23.0743C72.009 23.0743 71.8665 22.9299 71.8665 22.7221V12.4056H68.386C68.179 12.4056 68.0365 12.2618 68.0365 12.0539V10.6308C68.0365 10.4224 68.179 10.2783 68.386 10.2783H77.651C77.8575 10.2783 78 10.4226 78 10.6308V12.0538C78 12.2616 77.8575 12.4056 77.651 12.4056H74.1705V22.722C74.1705 22.9299 74.0275 23.0743 73.821 23.0743H72.216ZM45.5534 17.9236L43.7262 12.6458L41.8827 17.9236H45.5534ZM49.6058 22.6573C49.6856 22.8659 49.5583 23.0743 49.3358 23.0743H47.6834C47.4448 23.0743 47.3016 22.9625 47.2219 22.7376L46.2847 20.0195H41.1523L40.2134 22.7376C40.1346 22.9627 39.991 23.0743 39.7531 23.0743H38.1963C37.9894 23.0743 37.8462 22.8657 37.9256 22.6573L42.2322 10.5988C42.3119 10.3742 42.4548 10.2788 42.6927 10.2788H44.8222C45.0608 10.2788 45.2198 10.3742 45.2991 10.5988L49.6058 22.6573ZM61.95 21.3465C62.9665 21.3465 63.65 20.8515 64.2065 20.0033L61.6325 17.22C60.6465 17.7803 60.011 18.3393 60.011 19.4592C60.011 20.5631 60.9005 21.3465 61.95 21.3465ZM62.665 11.9736C61.8385 11.9736 61.362 12.502 61.362 13.2056C61.362 13.7493 61.6475 14.2292 62.2995 14.9332C63.4275 14.2769 63.9045 13.8773 63.9045 13.1737C63.9045 12.5174 63.4915 11.9736 62.665 11.9736ZM69.7055 22.6263C69.9115 22.8504 69.785 23.0743 69.53 23.0743H67.5115C67.2415 23.0743 67.0985 23.0099 66.924 22.8017L65.716 21.4588C64.9055 22.5465 63.7765 23.362 61.902 23.362C59.582 23.362 57.7535 21.9545 57.7535 19.5399C57.7535 17.6839 58.7395 16.692 60.2335 15.8605C59.502 15.0129 59.169 14.1171 59.169 13.3338C59.169 11.3499 60.5515 9.9906 62.6325 9.9906C64.7625 9.9906 66.0655 11.2547 66.0655 13.1256C66.0655 14.7251 64.9215 15.62 63.7135 16.2923L65.4935 18.2283L66.4945 16.4681C66.6215 16.2605 66.7645 16.1803 67.0185 16.1803H68.5595C68.8145 16.1803 68.9575 16.3567 68.799 16.6284L67.019 19.6989L69.7055 22.6263ZM53.846 23.0743C54.0525 23.0743 54.1965 22.9299 54.1965 22.7221V12.4056H57.676C57.8825 12.4056 58.0255 12.2618 58.0255 12.0539V10.6308C58.0255 10.4224 57.8825 10.2783 57.676 10.2783H48.4111C48.2042 10.2783 48.0615 10.4226 48.0615 10.6308V12.0538C48.0615 12.2616 48.2043 12.4056 48.4111 12.4056H51.8905V22.722C51.8905 22.9299 52.0345 23.0743 52.2405 23.0743H53.846Z"
fill="currentColor"
/>
<path
d="M6.81529 29.2973C9.51034 31.3978 12.8931 32.6598 16.5632 32.6598C20.5794 32.6598 24.2409 31.1585 27.0353 28.694C27.0692 28.6638 27.0525 28.644 27.0192 28.6638C25.7652 29.507 22.1913 31.3477 16.5633 31.3477C11.6724 31.3477 8.58149 30.2489 6.83599 29.268C6.80259 29.2513 6.79024 29.2765 6.81529 29.2973ZM17.6422 30.115C21.5541 30.115 25.8528 29.0414 28.4237 26.916C29.1273 26.3369 29.7974 25.5662 30.3976 24.5304C30.7432 23.9345 31.081 23.2264 31.3562 22.5304C31.3685 22.4964 31.3477 22.48 31.3224 22.5181C28.9316 26.0606 22.0082 28.2702 14.8596 28.2702C9.80669 28.2702 4.36979 26.6433 2.24122 23.5368C2.22027 23.508 2.19931 23.5204 2.21207 23.5533C4.19499 27.7969 10.2107 30.115 17.6422 30.115ZM13.3681 23.0752C5.23224 23.0752 1.39597 19.26 0.700069 16.6562C0.691489 16.6186 0.666824 16.6269 0.666824 16.6608C0.666824 17.5373 0.753954 18.6684 0.903864 19.4192C0.975379 19.7846 1.27079 20.3582 1.7039 20.8154C3.67398 22.883 8.58569 25.7802 17.092 25.7802C28.6813 25.7802 31.3313 21.8932 31.8724 20.6149C32.2593 19.7006 32.4598 18.0486 32.4598 16.6608C32.4598 16.325 32.4514 16.0568 32.4388 15.7934C32.4388 15.7506 32.4142 15.7472 32.4057 15.7888C31.8266 18.9166 21.9248 23.0752 13.3681 23.0752ZM2.19931 9.79709C1.73313 10.7287 1.21636 12.3002 1.06274 13.1136C0.995404 13.4621 1.02409 13.6295 1.14547 13.8896C2.12062 15.9728 7.05309 19.3058 18.5586 19.3058C25.5778 19.3058 31.0306 17.5696 31.914 14.401C32.0766 13.8178 32.0853 13.202 31.8764 12.3722C31.6429 11.4449 31.2056 10.3636 30.8356 9.60429C30.8233 9.57954 30.8017 9.58319 30.8061 9.61244C30.9436 13.7682 19.4334 16.4466 13.626 16.4466C7.33544 16.4466 2.08737 13.923 2.08737 10.7366C2.08737 10.4304 2.1503 10.1242 2.22885 9.8055C2.23674 9.7764 2.21199 9.77159 2.19931 9.79709ZM27.061 4.69469C27.1278 4.80009 27.1612 4.91257 27.1612 5.06398C27.1612 6.84164 21.7577 9.98634 13.1561 9.98634C6.83599 9.98634 5.65274 7.62564 5.65274 6.12434C5.65274 5.58771 5.85714 5.03861 6.30734 4.48093C6.33194 4.44777 6.31104 4.43501 6.28269 4.45967C5.46159 5.16027 4.70719 5.94859 4.04539 6.79959C3.72918 7.20204 3.53289 7.55854 3.53289 7.77214C3.53289 10.8833 11.2809 13.1391 18.5254 13.1391C26.2446 13.1391 29.6897 10.6018 29.6897 8.37209C29.6897 7.57524 29.3817 7.11004 28.5936 6.20814C28.082 5.62142 27.598 5.14369 27.0858 4.67343C27.061 4.65289 27.0438 4.66962 27.061 4.69469ZM24.6946 2.91743C22.3124 1.47966 19.5458 0.661682 16.5633 0.661682C13.56 0.661682 10.7102 1.50823 8.31934 2.98407C7.60214 3.42849 7.19854 3.78462 7.19854 4.24242C7.19854 5.59191 10.3308 7.04294 15.888 7.04294C21.3874 7.04294 25.653 5.45358 25.653 3.92373C25.653 3.55855 25.336 3.30307 24.6946 2.91743Z"
fill="currentColor"
/>
</svg>
<svg fill="none" height="29" viewBox="0 0 50 29" width="50" xmlns="http://www.w3.org/2000/svg">
<path
clipRule="evenodd"
d="M39.9999 0V21.8032H34.8354V1.08992L39.9999 0ZM25.2944 23.8694C26.7203 23.8694 27.8771 25.0181 27.8771 26.4351C27.8763 27.8513 26.7203 29 25.2944 29C23.8686 29 22.7118 27.8513 22.7118 26.4351C22.7118 25.0181 23.8678 23.8694 25.2944 23.8694ZM25.2944 6.1915C29.692 6.1915 33.2638 9.73353 33.2638 14.1085C33.2638 18.4827 29.692 22.0247 25.2944 22.0247C20.8897 22.0247 17.3243 18.4835 17.3243 14.1077C17.3243 9.73353 20.8969 6.1915 25.2944 6.1915ZM25.2944 16.6734C25.6325 16.6748 25.9675 16.6095 26.2803 16.4813C26.5931 16.3531 26.8776 16.1645 27.1174 15.9263C27.3573 15.6881 27.5478 15.4049 27.6782 15.093C27.8085 14.7811 27.8761 14.4466 27.8771 14.1085C27.8762 13.7704 27.8087 13.4357 27.6784 13.1237C27.5481 12.8117 27.3576 12.5285 27.1177 12.2902C26.8778 12.0519 26.5933 11.8632 26.2805 11.735C25.9676 11.6067 25.6326 11.5414 25.2944 11.5428C24.9564 11.5414 24.6214 11.6067 24.3086 11.7349C23.9958 11.8631 23.7113 12.0517 23.4715 12.2899C23.2316 12.5281 23.041 12.8113 22.9107 13.1232C22.7804 13.4351 22.7128 13.7696 22.7118 14.1077C22.7127 14.4458 22.7802 14.7804 22.9105 15.0925C23.0408 15.4045 23.2313 15.6877 23.4712 15.926C23.711 16.1643 23.9955 16.353 24.3084 16.4812C24.6213 16.6095 24.9563 16.6748 25.2944 16.6734ZM9.04639 16.6734C9.47271 16.674 9.8819 16.5057 10.1843 16.2052C10.4868 15.9047 10.6578 15.4966 10.6599 15.0703C10.6578 14.6439 10.4867 14.2357 10.1841 13.9352C9.88145 13.6347 9.47204 13.4664 9.04558 13.4673H5.17247V16.6734H9.04639ZM5.17167 5.12978V8.33589H8.04186C8.46818 8.33653 8.87738 8.16818 9.17981 7.86771C9.48224 7.56724 9.65326 7.15915 9.65539 6.73283C9.65326 6.30652 9.48224 5.89843 9.17981 5.59796C8.87738 5.29749 8.46818 5.12913 8.04186 5.12978H5.17167ZM13.8024 10.2749C15.051 11.5001 15.8243 13.1958 15.8179 15.0703C15.8179 18.7896 12.7834 21.8032 9.03914 21.8032H0V0H8.03461C11.7788 0 14.8134 3.01439 14.8134 6.73364C14.8134 8.03058 14.4476 9.24858 13.8024 10.2749ZM49.7559 11.6145H47.1814V15.6399C47.1814 16.8587 47.5761 17.7561 48.6096 17.7561C49.2766 17.7561 49.764 17.6062 49.764 17.6062V21.3827C49.764 21.3827 48.695 22.0247 47.2466 22.0247H47.1822C47.1178 22.0247 47.0598 22.0166 46.9953 22.0166H46.9446C46.9164 22.0166 46.8801 22.0102 46.8519 22.0102C43.968 21.8604 42.0097 20.0575 42.0097 16.9296V3.65561L47.1741 2.56489V6.48472H49.7568V11.6153L49.7559 11.6145Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
<svg fill="none" height="19" viewBox="0 0 119 19" width="119" xmlns="http://www.w3.org/2000/svg">
<path
clipRule="evenodd"
d="M64.965 8.55517C64.965 8.55517 63.8656 7.85669 63.1858 7.58642C63.1858 7.58642 61.0815 6.58434 60.664 6.10757C60.664 6.10757 59.8439 5.31343 60.3241 4.39105C60.3241 4.39105 60.5251 3.78749 61.3918 3.78749C61.3918 3.78749 62.5201 3.85125 62.5201 4.80406V6.14814H66.5637L66.5567 4.16861C66.5567 4.16861 66.8676 0.926878 61.7938 0.784137C61.7938 0.784137 57.8031 0.514593 56.6585 2.72309C56.6585 2.72309 56.2093 3.2158 56.2093 4.93159V6.17133C56.2093 6.17133 56.1635 7.60164 56.8743 8.52329C56.8743 8.52329 57.2763 9.12758 58.2664 9.79419C58.2664 9.79419 60.2769 10.9064 61.4835 11.5433C61.4835 11.5433 62.7013 12.2577 62.5413 13.3946C62.5413 13.3946 62.4418 14.5619 61.2656 14.5141C61.2656 14.5141 60.1901 14.4648 60.1901 13.32V11.9766H55.8814V13.9279C55.8814 13.9279 55.7608 17.6594 61.2973 17.6594C61.2973 17.6594 66.5877 17.7696 66.8345 13.8301V12.2411C66.8338 12.2411 67.0214 9.8268 64.965 8.55517ZM44.2633 1.24859L42.9093 9.96664H42.5927L41.2994 1.32684H34.545L34.2065 17.1145H38.2057L38.2536 5.21924H38.5681L40.688 17.1124H44.8952L46.9889 5.22359H47.2674L47.3641 17.1145H51.3844L50.9514 1.24859H44.2633ZM20.4573 1.31453L17.7966 17.0964H22.1018L23.678 4.85986H24.032L25.6074 17.0964H29.9119L27.2519 1.31453H20.4573ZM110.794 8.3378V10.6659H111.899V13.1649C111.899 14.3865 110.882 14.409 110.882 14.409C109.647 14.409 109.693 13.2482 109.693 13.2482V4.60408C109.693 3.71358 110.79 3.66576 110.79 3.66576C111.842 3.66576 111.849 4.72798 111.849 4.72798V6.04235H115.903C116.035 3.47664 115.537 2.85569 115.537 2.85569C114.532 0.53561 110.728 0.66241 110.728 0.66241C104.883 0.66241 105.531 5.27069 105.531 5.27069V13.6286C105.657 17.9507 111.417 17.5116 111.514 17.508C114.076 17.2167 114.85 16.3798 114.85 16.3798C115.562 15.8364 115.745 15.0836 115.745 15.0836C115.949 14.6452 116 13.2482 116 13.2482V8.3378H110.794ZM97.3582 10.9064H97.1819L93.0685 1.25293H88.1469V17.116H92.1524L91.9127 7.46397H92.0918L96.3533 17.116H101.125V1.25148H97.069L97.3582 10.9064ZM78.3694 13.1635C78.3694 13.1635 78.4272 14.4655 77.2524 14.4655C77.2524 14.4655 76.0168 14.5336 76.0168 13.1961L76.0042 1.26235H71.6108V13.1011C71.6108 13.1011 71.1602 17.5819 77.3144 17.5819C77.3144 17.5819 82.6626 17.6471 82.6626 13.3069V1.26308H78.3694V13.1635ZM12.0817 8.55517C12.0817 8.55517 10.9837 7.85669 10.3032 7.58714C10.3032 7.58714 8.20032 6.58579 7.78285 6.10829C7.78285 6.10829 6.9627 5.31271 7.44365 4.3925C7.44365 4.3925 7.64392 3.78893 8.5099 3.78893C8.5099 3.78893 9.63892 3.85197 9.63892 4.80478V6.14887H13.6839L13.6762 4.16861C13.6762 4.16861 13.985 0.926878 8.91327 0.784861C8.91327 0.784861 8.53106 0.759499 7.97043 0.788482C7.97043 0.788482 4.89789 0.958033 3.7992 2.68976C3.79285 2.7028 3.78368 2.71223 3.77734 2.72382C3.77734 2.72382 3.32883 3.2158 3.32883 4.93231V6.17205C3.32883 6.17205 3.28229 7.60236 3.99383 8.52402C3.99383 8.52402 4.39579 9.12831 5.38589 9.79491C5.38589 9.79491 7.39569 10.9071 8.60228 11.544C8.60228 11.544 9.82156 12.257 9.66008 13.3953C9.66008 13.3953 9.56064 14.5626 8.38508 14.5148C8.38508 14.5148 7.30966 14.4655 7.30966 13.3207V11.9766H3.00021V13.9293C3.00021 13.9293 2.87962 17.6609 8.41611 17.6609C8.41611 17.6609 13.7051 17.771 13.954 13.8315V12.244C13.954 12.2425 14.1388 9.8268 12.0817 8.55517Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
<svg fill="none" height="36" viewBox="0 0 59 36" width="59" xmlns="http://www.w3.org/2000/svg">
<path
d="M16.7684 12.8869C16.7684 13.6049 16.8461 14.1872 16.9819 14.6142C17.1372 15.0411 17.3313 15.5069 17.603 16.0115C17.7 16.1668 17.7388 16.3221 17.7388 16.4579C17.7388 16.652 17.6224 16.8461 17.3701 17.0401L16.1474 17.8553C15.9727 17.9717 15.798 18.0299 15.6428 18.0299C15.4487 18.0299 15.2546 17.9329 15.0605 17.7582C14.7888 17.4671 14.5559 17.1566 14.3619 16.8461C14.1678 16.5161 13.9737 16.1474 13.7602 15.701C12.2464 17.4865 10.3444 18.3793 8.05429 18.3793C6.42402 18.3793 5.12369 17.9135 4.17271 16.9819C3.22172 16.0503 2.73652 14.8082 2.73652 13.2556C2.73652 11.6059 3.31876 10.2668 4.50264 9.25758C5.68652 8.24837 7.25856 7.74376 9.25758 7.74376C9.91744 7.74376 10.5967 7.80199 11.3148 7.89903C12.0329 7.99607 12.7704 8.15133 13.5467 8.326V6.90922C13.5467 5.43422 13.2362 4.40561 12.6345 3.80396C12.0135 3.20232 10.9655 2.9112 9.47106 2.9112C8.79179 2.9112 8.0931 2.98883 7.37501 3.1635C6.65692 3.33817 5.95823 3.55166 5.27896 3.82337C4.96843 3.95922 4.73554 4.03686 4.59968 4.07567C4.46383 4.11449 4.36679 4.1339 4.28916 4.1339C4.01744 4.1339 3.88159 3.93982 3.88159 3.53225V2.58126C3.88159 2.27074 3.9204 2.03784 4.01744 1.90199C4.11448 1.76613 4.28915 1.63028 4.56087 1.49442C5.24014 1.14508 6.05527 0.853961 7.00626 0.621067C7.95725 0.368764 8.96646 0.252317 10.0339 0.252317C12.3434 0.252317 14.0319 0.77633 15.1188 1.82436C16.1862 2.87238 16.7296 4.46383 16.7296 6.5987V12.8869H16.7684ZM8.88883 15.8369C9.52929 15.8369 10.1892 15.7204 10.8878 15.4875C11.5865 15.2546 12.2076 14.8276 12.7316 14.2454C13.0421 13.8767 13.275 13.4691 13.3915 13.0033C13.5079 12.5375 13.5855 11.9747 13.5855 11.3148V10.4997C13.0227 10.3638 12.4211 10.2474 11.8 10.1698C11.179 10.0921 10.5773 10.0533 9.97567 10.0533C8.67534 10.0533 7.72435 10.3056 7.08389 10.8296C6.44343 11.3536 6.13291 12.0911 6.13291 13.0615C6.13291 13.9737 6.3658 14.653 6.851 15.1188C7.31679 15.604 7.99606 15.8369 8.88883 15.8369ZM24.4734 17.9329C24.124 17.9329 23.8911 17.8747 23.7359 17.7388C23.5806 17.6224 23.4447 17.3507 23.3283 16.9819L18.7674 1.97962C18.651 1.59146 18.5928 1.33916 18.5928 1.2033C18.5928 0.892777 18.748 0.718106 19.0586 0.718106H20.9605C21.3293 0.718106 21.5816 0.77633 21.7174 0.912185C21.8727 1.02863 21.9892 1.30034 22.1056 1.66909L25.3661 14.5171L28.3938 1.66909C28.4908 1.28094 28.6072 1.02863 28.7625 0.912185C28.9178 0.795738 29.1895 0.718106 29.5388 0.718106H31.0915C31.4602 0.718106 31.7125 0.77633 31.8678 0.912185C32.023 1.02863 32.1589 1.30034 32.2365 1.66909L35.303 14.6724L38.6605 1.66909C38.777 1.28094 38.9128 1.02863 39.0487 0.912185C39.204 0.795738 39.4563 0.718106 39.8056 0.718106H41.6105C41.9211 0.718106 42.0957 0.873369 42.0957 1.2033C42.0957 1.30034 42.0763 1.39738 42.0569 1.51383C42.0375 1.63028 41.9987 1.78554 41.9211 1.99903L37.2438 17.0013C37.1273 17.3895 36.9915 17.6418 36.8362 17.7582C36.6809 17.8747 36.4286 17.9523 36.0987 17.9523H34.4296C34.0609 17.9523 33.8086 17.8941 33.6533 17.7582C33.498 17.6224 33.3622 17.3701 33.2845 16.9819L30.2763 4.46383L27.2875 16.9625C27.1905 17.3507 27.074 17.603 26.9188 17.7388C26.7635 17.8747 26.4918 17.9329 26.1424 17.9329H24.4734ZM49.4125 18.4569C48.4033 18.4569 47.3941 18.3405 46.4237 18.1076C45.4533 17.8747 44.6964 17.6224 44.1918 17.3313C43.8813 17.1566 43.6678 16.9625 43.5901 16.7878C43.5125 16.6132 43.4737 16.4191 43.4737 16.2444V15.2546C43.4737 14.8471 43.629 14.653 43.9201 14.653C44.0365 14.653 44.153 14.6724 44.2694 14.7112C44.3859 14.75 44.5605 14.8276 44.7546 14.9053C45.4145 15.1964 46.1326 15.4293 46.8895 15.5846C47.6658 15.7398 48.4227 15.8174 49.199 15.8174C50.4217 15.8174 51.3727 15.604 52.0326 15.177C52.6924 14.75 53.0418 14.129 53.0418 13.3332C53.0418 12.7898 52.8671 12.3434 52.5178 11.9747C52.1684 11.6059 51.5086 11.276 50.5576 10.9655L47.7434 10.0921C46.3267 9.64574 45.2786 8.98587 44.6382 8.11251C43.9977 7.25857 43.6678 6.30758 43.6678 5.29837C43.6678 4.48324 43.8424 3.76515 44.1918 3.14409C44.5411 2.52304 45.0069 1.97962 45.5891 1.55265C46.1714 1.10626 46.8313 0.77633 47.6076 0.543435C48.3839 0.31054 49.199 0.213501 50.053 0.213501C50.4799 0.213501 50.9263 0.232909 51.3533 0.291133C51.7997 0.349356 52.2072 0.426988 52.6148 0.504619C53.003 0.601659 53.3717 0.698698 53.7211 0.815146C54.0704 0.931593 54.3421 1.04804 54.5362 1.16449C54.8079 1.31975 55.002 1.47501 55.1184 1.64969C55.2349 1.80495 55.2931 2.01844 55.2931 2.29015V3.20232C55.2931 3.60988 55.1378 3.82337 54.8467 3.82337C54.6915 3.82337 54.4391 3.74574 54.1092 3.59047C53.003 3.08587 51.7609 2.83357 50.3829 2.83357C49.2767 2.83357 48.4033 3.00824 47.8017 3.37699C47.2 3.74574 46.8895 4.30857 46.8895 5.10429C46.8895 5.64771 47.0836 6.1135 47.4717 6.48225C47.8599 6.851 48.578 7.21975 49.6066 7.54969L52.3625 8.42304C53.7599 8.86942 54.7691 9.49047 55.3707 10.2862C55.9724 11.0819 56.2635 11.9941 56.2635 13.0033C56.2635 13.8378 56.0888 14.5948 55.7589 15.2546C55.4095 15.9145 54.9438 16.4967 54.3421 16.9625C53.7405 17.4477 53.0224 17.7971 52.1878 18.0494C51.3145 18.3211 50.4023 18.4569 49.4125 18.4569Z"
fill="currentColor"
/>
<path
clipRule="evenodd"
d="M53.0806 27.8892C46.6954 32.6053 37.4184 35.1089 29.4418 35.1089C18.2628 35.1089 8.19014 30.975 0.582247 24.1046C-0.0193978 23.5612 0.524023 22.8237 1.24212 23.2507C9.47106 28.025 19.6214 30.9168 30.1211 30.9168C37.2049 30.9168 44.9875 29.4418 52.149 26.4142C53.2165 25.929 54.1286 27.1128 53.0806 27.8892Z"
fill="currentColor"
fillRule="evenodd"
/>
<path
clipRule="evenodd"
d="M55.7395 24.8615C54.9244 23.8135 50.3441 24.3569 48.2674 24.6092C47.6464 24.6869 47.5494 24.1434 48.1122 23.7359C51.7609 21.174 57.7579 21.9115 58.4566 22.7655C59.1553 23.6388 58.2625 29.6359 54.8467 32.5082C54.3227 32.9546 53.8181 32.7217 54.051 32.1395C54.8273 30.2181 56.5546 25.8901 55.7395 24.8615Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
<svg fill="none" height="18" viewBox="0 0 91 18" width="91" xmlns="http://www.w3.org/2000/svg">
<path
clipRule="evenodd"
d="M9.31948 0.161179C14.2843 0.641702 19.7297 3.12384 22.8531 6.72777C26.8569 11.2922 25.496 15.8571 19.8098 16.8977C14.1241 18.0189 6.27616 15.1364 2.27237 10.5714C-0.77095 7.04755 -0.690299 3.52428 2.11219 1.60275L17.8877 13.2944L9.31948 0.161179Z"
fill="currentColor"
fillRule="evenodd"
/>
<path
clipRule="evenodd"
d="M87.4761 16.8977L85.4745 8.88956C85.2331 7.92908 85.0735 6.96803 84.9139 6.08707H84.8332C84.6736 7.04812 84.593 7.92908 84.3527 8.96965L82.35 16.8977H78.9068L83.4718 0.24127H86.1947L91 16.8977H87.4761ZM34.9445 0.24127L36.9467 8.24887C37.2671 9.20991 37.3472 10.1715 37.5073 11.0514H37.5874C37.7476 10.0909 37.8277 9.12926 38.0679 8.16878L40.0701 0.24127H43.5133L38.9489 16.8977H36.2259L31.4213 0.24127H34.9445ZM45.9949 0.24127H49.2785V16.8977H45.9949V0.24127ZM67.8576 0.24127L69.5383 10.0914L70.8998 0.24127H75.304L76.9858 16.8977H73.6227L72.8219 4.88577L72.2612 8.96965L70.8998 16.8977H68.3375L66.7358 8.88956L66.1752 5.20612V4.88577H66.0951L65.6146 16.8977H62.0907L63.5329 0.24127H67.8576ZM57.4468 0.24127C56.8862 0.641706 56.4863 1.36193 56.166 2.08271C55.285 4.08489 56.2455 5.68607 57.0464 7.12764C57.127 7.28782 57.2877 7.52808 57.3667 7.76834C59.2087 10.8111 59.8494 13.053 58.5686 16.2559L58.2482 16.8972H54.2439C54.7244 16.4962 55.2044 15.9367 55.4446 15.2954C56.4062 13.1331 55.285 11.1314 54.2439 9.1287C53.0431 6.80673 51.7612 4.40468 53.1226 1.1211C53.2033 0.800751 53.5237 0.240143 53.5237 0.240143H57.4468V0.24127Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
</Stack>
</Stack>
</Box>
<Box sx={{ boxShadow: 'var(--mui-shadows-8)', display: 'flex', flexDirection: 'column' }}>
<Box
sx={{
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
flex: '1 1 auto',
justifyContent: 'center',
p: 3,
}}
>
<Box sx={{ maxWidth: '420px', width: '100%' }}>{children}</Box>
</Box>
</Box>
</Box>
);
}

View File

@@ -0,0 +1,207 @@
'use client';
import * as React from 'react';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import LoginAvif from './login_bg.avif';
export interface SplitLayoutProps {
children: React.ReactNode;
}
export function SplitLayout({ children }: SplitLayoutProps): React.JSX.Element {
const { t } = useTranslation(['sign_in']);
return (
<Box
sx={{
display: 'grid',
gridTemplateColumns: { xs: '1fr', lg: '1fr 800px' },
minHeight: '100vh',
//
}}
>
<Box>
<Box
sx={{
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'var(--mui-palette-background-level1)',
display: { xs: 'none', lg: 'flex' },
flexDirection: 'column',
p: 3,
//
minHeight: '100vh',
height: '100vh',
//
backgroundImage: `url(${LoginAvif.src})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
opacity: 0.25,
}}
/>
<Box
sx={{
alignItems: 'center',
justifyContent: 'center',
// bgcolor: 'var(--mui-palette-background-level1)',
display: { xs: 'none', lg: 'flex' },
flexDirection: 'column',
//
height: 0,
position: 'relative',
top: '-50vh',
//
backgroundColor: 'gold',
//
}}
>
<Stack
spacing={4}
sx={{ maxWidth: '500px', opacity: 1 }}
>
<Stack spacing={1}>
<Typography variant="h4">{t('welcome-title')}</Typography>
<Typography color="text.secondary">{t('welcome-notes')}</Typography>
</Stack>
<Stack
direction="row"
spacing={3}
sx={{ alignItems: 'center', color: 'var(--mui-palette-neutral-500)', flexWrap: 'wrap' }}
>
<svg
fill="none"
height="36"
viewBox="0 0 133 36"
width="133"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M77.4069 9.92864L85.0581 7.0798L77.4069 4.14957V0.161194L90.5116 5.45189V8.70771L77.4069 13.9984V9.92864Z"
fill="currentColor"
/>
<path
d="M5.04645 35.1612C2.27901 35.1612 -6.10352e-05 33.7775 -6.10352e-05 30.6844V30.5216C-6.10352e-05 26.7775 3.25575 25.4751 7.24413 25.4751H9.11622V24.7426C9.11622 23.1961 8.46506 22.3007 6.83715 22.3007C5.37203 22.3007 4.63947 23.1147 4.55808 24.2542H0.488311C0.813893 20.8356 3.49994 19.2077 7.08134 19.2077C10.7441 19.2077 13.4302 20.7542 13.4302 24.5798V34.8356H9.27901V33.0449C8.46506 34.1844 7.08134 35.1612 5.04645 35.1612ZM9.11622 29.7891V28.324H7.40692C5.29064 28.324 4.2325 28.8937 4.2325 30.2775V30.4403C4.2325 31.4984 4.88366 32.231 6.34878 32.231C7.81389 32.1496 9.11622 31.3356 9.11622 29.7891ZM23.1162 35.1612C18.8837 35.1612 15.7906 32.5565 15.7906 27.3472V27.103C15.7906 21.8937 19.0465 19.1263 23.1162 19.1263C26.6162 19.1263 29.4651 20.917 29.7906 24.9054H25.7209C25.4767 23.4403 24.6627 22.4635 23.1976 22.4635C21.4069 22.4635 20.1046 23.9286 20.1046 26.9403V27.4286C20.1046 30.5216 21.2441 31.9054 23.1976 31.9054C24.6627 31.9054 25.7209 30.8472 25.9651 29.1379H29.872C29.6279 32.7193 27.2674 35.1612 23.1162 35.1612ZM39.0697 35.1612C34.8372 35.1612 31.7441 32.5565 31.7441 27.3472V27.103C31.7441 21.8937 34.9999 19.1263 39.0697 19.1263C42.5697 19.1263 45.4186 20.917 45.7441 24.9054H41.6744C41.4302 23.4403 40.6162 22.4635 39.1511 22.4635C37.3604 22.4635 36.0581 23.9286 36.0581 26.9403V27.4286C36.0581 30.5216 37.1976 31.9054 39.1511 31.9054C40.6162 31.9054 41.6744 30.8472 41.9186 29.1379H45.8255C45.5813 32.7193 43.2209 35.1612 39.0697 35.1612ZM55.1046 35.1612C50.7093 35.1612 47.6976 32.5565 47.6976 27.4286V27.103C47.6976 21.9751 50.872 19.1263 55.0232 19.1263C58.8488 19.1263 62.0232 21.2426 62.0232 26.3705V28.2426H52.0116C52.1744 31.01 53.3953 32.0682 55.186 32.0682C56.8139 32.0682 57.7093 31.1728 58.0348 30.1147H62.0232C61.5348 32.9635 59.093 35.1612 55.1046 35.1612ZM52.093 25.3937H57.7907C57.7093 23.1147 56.6511 22.1379 54.9418 22.1379C53.6395 22.2193 52.4186 22.9519 52.093 25.3937ZM64.6279 19.5333H68.9418V21.8123C69.6744 20.3472 71.2209 19.2077 73.5814 19.2077C76.3488 19.2077 78.2209 20.917 78.2209 24.5798V34.8356H73.9069V25.2309C73.9069 23.4403 73.1744 22.6263 71.6279 22.6263C70.1627 22.6263 68.9418 23.5216 68.9418 25.4751V34.8356H64.6279V19.5333ZM86.1162 14.8937V19.5333H89.0465V22.7077H86.1162V29.9519C86.1162 31.0914 86.6046 31.6612 87.6627 31.6612C88.3139 31.6612 88.7209 31.5798 89.1279 31.417V34.7542C88.6395 34.917 87.7441 35.0798 86.686 35.0798C83.3488 35.0798 81.8023 33.5333 81.8023 30.4403V22.7077H80.0116V19.5333H81.8023V16.6844L86.1162 14.8937ZM105.163 34.8356H100.93V32.5565C100.198 34.0216 98.7325 35.1612 96.4535 35.1612C93.686 35.1612 91.6511 33.4519 91.6511 29.8705V19.5333H95.9651V29.3007C95.9651 31.0914 96.6976 31.9054 98.1628 31.9054C99.6279 31.9054 100.849 30.9286 100.849 29.0565V19.5333H105.163V34.8356ZM108.337 19.5333H112.651V22.3821C113.546 20.3472 115.012 19.3705 117.291 19.3705V23.603C114.36 23.603 112.651 24.4984 112.651 27.0216V34.917H108.337V19.5333ZM126 35.1612C121.605 35.1612 118.593 32.5565 118.593 27.4286V27.103C118.593 21.9751 121.767 19.1263 125.919 19.1263C129.744 19.1263 132.919 21.2426 132.919 26.3705V28.2426H122.988C123.151 31.01 124.372 32.0682 126.163 32.0682C127.791 32.0682 128.686 31.1728 129.012 30.1147H133C132.349 32.9635 129.988 35.1612 126 35.1612ZM122.907 25.3937H128.686C128.605 23.1147 127.546 22.1379 125.837 22.1379C124.535 22.2193 123.314 22.9519 122.907 25.3937Z"
fill="currentColor"
/>
</svg>
<svg
fill="none"
height="33"
viewBox="0 0 78 33"
width="78"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M72.216 23.0743C72.009 23.0743 71.8665 22.9299 71.8665 22.7221V12.4056H68.386C68.179 12.4056 68.0365 12.2618 68.0365 12.0539V10.6308C68.0365 10.4224 68.179 10.2783 68.386 10.2783H77.651C77.8575 10.2783 78 10.4226 78 10.6308V12.0538C78 12.2616 77.8575 12.4056 77.651 12.4056H74.1705V22.722C74.1705 22.9299 74.0275 23.0743 73.821 23.0743H72.216ZM45.5534 17.9236L43.7262 12.6458L41.8827 17.9236H45.5534ZM49.6058 22.6573C49.6856 22.8659 49.5583 23.0743 49.3358 23.0743H47.6834C47.4448 23.0743 47.3016 22.9625 47.2219 22.7376L46.2847 20.0195H41.1523L40.2134 22.7376C40.1346 22.9627 39.991 23.0743 39.7531 23.0743H38.1963C37.9894 23.0743 37.8462 22.8657 37.9256 22.6573L42.2322 10.5988C42.3119 10.3742 42.4548 10.2788 42.6927 10.2788H44.8222C45.0608 10.2788 45.2198 10.3742 45.2991 10.5988L49.6058 22.6573ZM61.95 21.3465C62.9665 21.3465 63.65 20.8515 64.2065 20.0033L61.6325 17.22C60.6465 17.7803 60.011 18.3393 60.011 19.4592C60.011 20.5631 60.9005 21.3465 61.95 21.3465ZM62.665 11.9736C61.8385 11.9736 61.362 12.502 61.362 13.2056C61.362 13.7493 61.6475 14.2292 62.2995 14.9332C63.4275 14.2769 63.9045 13.8773 63.9045 13.1737C63.9045 12.5174 63.4915 11.9736 62.665 11.9736ZM69.7055 22.6263C69.9115 22.8504 69.785 23.0743 69.53 23.0743H67.5115C67.2415 23.0743 67.0985 23.0099 66.924 22.8017L65.716 21.4588C64.9055 22.5465 63.7765 23.362 61.902 23.362C59.582 23.362 57.7535 21.9545 57.7535 19.5399C57.7535 17.6839 58.7395 16.692 60.2335 15.8605C59.502 15.0129 59.169 14.1171 59.169 13.3338C59.169 11.3499 60.5515 9.9906 62.6325 9.9906C64.7625 9.9906 66.0655 11.2547 66.0655 13.1256C66.0655 14.7251 64.9215 15.62 63.7135 16.2923L65.4935 18.2283L66.4945 16.4681C66.6215 16.2605 66.7645 16.1803 67.0185 16.1803H68.5595C68.8145 16.1803 68.9575 16.3567 68.799 16.6284L67.019 19.6989L69.7055 22.6263ZM53.846 23.0743C54.0525 23.0743 54.1965 22.9299 54.1965 22.7221V12.4056H57.676C57.8825 12.4056 58.0255 12.2618 58.0255 12.0539V10.6308C58.0255 10.4224 57.8825 10.2783 57.676 10.2783H48.4111C48.2042 10.2783 48.0615 10.4226 48.0615 10.6308V12.0538C48.0615 12.2616 48.2043 12.4056 48.4111 12.4056H51.8905V22.722C51.8905 22.9299 52.0345 23.0743 52.2405 23.0743H53.846Z"
fill="currentColor"
/>
<path
d="M6.81529 29.2973C9.51034 31.3978 12.8931 32.6598 16.5632 32.6598C20.5794 32.6598 24.2409 31.1585 27.0353 28.694C27.0692 28.6638 27.0525 28.644 27.0192 28.6638C25.7652 29.507 22.1913 31.3477 16.5633 31.3477C11.6724 31.3477 8.58149 30.2489 6.83599 29.268C6.80259 29.2513 6.79024 29.2765 6.81529 29.2973ZM17.6422 30.115C21.5541 30.115 25.8528 29.0414 28.4237 26.916C29.1273 26.3369 29.7974 25.5662 30.3976 24.5304C30.7432 23.9345 31.081 23.2264 31.3562 22.5304C31.3685 22.4964 31.3477 22.48 31.3224 22.5181C28.9316 26.0606 22.0082 28.2702 14.8596 28.2702C9.80669 28.2702 4.36979 26.6433 2.24122 23.5368C2.22027 23.508 2.19931 23.5204 2.21207 23.5533C4.19499 27.7969 10.2107 30.115 17.6422 30.115ZM13.3681 23.0752C5.23224 23.0752 1.39597 19.26 0.700069 16.6562C0.691489 16.6186 0.666824 16.6269 0.666824 16.6608C0.666824 17.5373 0.753954 18.6684 0.903864 19.4192C0.975379 19.7846 1.27079 20.3582 1.7039 20.8154C3.67398 22.883 8.58569 25.7802 17.092 25.7802C28.6813 25.7802 31.3313 21.8932 31.8724 20.6149C32.2593 19.7006 32.4598 18.0486 32.4598 16.6608C32.4598 16.325 32.4514 16.0568 32.4388 15.7934C32.4388 15.7506 32.4142 15.7472 32.4057 15.7888C31.8266 18.9166 21.9248 23.0752 13.3681 23.0752ZM2.19931 9.79709C1.73313 10.7287 1.21636 12.3002 1.06274 13.1136C0.995404 13.4621 1.02409 13.6295 1.14547 13.8896C2.12062 15.9728 7.05309 19.3058 18.5586 19.3058C25.5778 19.3058 31.0306 17.5696 31.914 14.401C32.0766 13.8178 32.0853 13.202 31.8764 12.3722C31.6429 11.4449 31.2056 10.3636 30.8356 9.60429C30.8233 9.57954 30.8017 9.58319 30.8061 9.61244C30.9436 13.7682 19.4334 16.4466 13.626 16.4466C7.33544 16.4466 2.08737 13.923 2.08737 10.7366C2.08737 10.4304 2.1503 10.1242 2.22885 9.8055C2.23674 9.7764 2.21199 9.77159 2.19931 9.79709ZM27.061 4.69469C27.1278 4.80009 27.1612 4.91257 27.1612 5.06398C27.1612 6.84164 21.7577 9.98634 13.1561 9.98634C6.83599 9.98634 5.65274 7.62564 5.65274 6.12434C5.65274 5.58771 5.85714 5.03861 6.30734 4.48093C6.33194 4.44777 6.31104 4.43501 6.28269 4.45967C5.46159 5.16027 4.70719 5.94859 4.04539 6.79959C3.72918 7.20204 3.53289 7.55854 3.53289 7.77214C3.53289 10.8833 11.2809 13.1391 18.5254 13.1391C26.2446 13.1391 29.6897 10.6018 29.6897 8.37209C29.6897 7.57524 29.3817 7.11004 28.5936 6.20814C28.082 5.62142 27.598 5.14369 27.0858 4.67343C27.061 4.65289 27.0438 4.66962 27.061 4.69469ZM24.6946 2.91743C22.3124 1.47966 19.5458 0.661682 16.5633 0.661682C13.56 0.661682 10.7102 1.50823 8.31934 2.98407C7.60214 3.42849 7.19854 3.78462 7.19854 4.24242C7.19854 5.59191 10.3308 7.04294 15.888 7.04294C21.3874 7.04294 25.653 5.45358 25.653 3.92373C25.653 3.55855 25.336 3.30307 24.6946 2.91743Z"
fill="currentColor"
/>
</svg>
<svg
fill="none"
height="29"
viewBox="0 0 50 29"
width="50"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M39.9999 0V21.8032H34.8354V1.08992L39.9999 0ZM25.2944 23.8694C26.7203 23.8694 27.8771 25.0181 27.8771 26.4351C27.8763 27.8513 26.7203 29 25.2944 29C23.8686 29 22.7118 27.8513 22.7118 26.4351C22.7118 25.0181 23.8678 23.8694 25.2944 23.8694ZM25.2944 6.1915C29.692 6.1915 33.2638 9.73353 33.2638 14.1085C33.2638 18.4827 29.692 22.0247 25.2944 22.0247C20.8897 22.0247 17.3243 18.4835 17.3243 14.1077C17.3243 9.73353 20.8969 6.1915 25.2944 6.1915ZM25.2944 16.6734C25.6325 16.6748 25.9675 16.6095 26.2803 16.4813C26.5931 16.3531 26.8776 16.1645 27.1174 15.9263C27.3573 15.6881 27.5478 15.4049 27.6782 15.093C27.8085 14.7811 27.8761 14.4466 27.8771 14.1085C27.8762 13.7704 27.8087 13.4357 27.6784 13.1237C27.5481 12.8117 27.3576 12.5285 27.1177 12.2902C26.8778 12.0519 26.5933 11.8632 26.2805 11.735C25.9676 11.6067 25.6326 11.5414 25.2944 11.5428C24.9564 11.5414 24.6214 11.6067 24.3086 11.7349C23.9958 11.8631 23.7113 12.0517 23.4715 12.2899C23.2316 12.5281 23.041 12.8113 22.9107 13.1232C22.7804 13.4351 22.7128 13.7696 22.7118 14.1077C22.7127 14.4458 22.7802 14.7804 22.9105 15.0925C23.0408 15.4045 23.2313 15.6877 23.4712 15.926C23.711 16.1643 23.9955 16.353 24.3084 16.4812C24.6213 16.6095 24.9563 16.6748 25.2944 16.6734ZM9.04639 16.6734C9.47271 16.674 9.8819 16.5057 10.1843 16.2052C10.4868 15.9047 10.6578 15.4966 10.6599 15.0703C10.6578 14.6439 10.4867 14.2357 10.1841 13.9352C9.88145 13.6347 9.47204 13.4664 9.04558 13.4673H5.17247V16.6734H9.04639ZM5.17167 5.12978V8.33589H8.04186C8.46818 8.33653 8.87738 8.16818 9.17981 7.86771C9.48224 7.56724 9.65326 7.15915 9.65539 6.73283C9.65326 6.30652 9.48224 5.89843 9.17981 5.59796C8.87738 5.29749 8.46818 5.12913 8.04186 5.12978H5.17167ZM13.8024 10.2749C15.051 11.5001 15.8243 13.1958 15.8179 15.0703C15.8179 18.7896 12.7834 21.8032 9.03914 21.8032H0V0H8.03461C11.7788 0 14.8134 3.01439 14.8134 6.73364C14.8134 8.03058 14.4476 9.24858 13.8024 10.2749ZM49.7559 11.6145H47.1814V15.6399C47.1814 16.8587 47.5761 17.7561 48.6096 17.7561C49.2766 17.7561 49.764 17.6062 49.764 17.6062V21.3827C49.764 21.3827 48.695 22.0247 47.2466 22.0247H47.1822C47.1178 22.0247 47.0598 22.0166 46.9953 22.0166H46.9446C46.9164 22.0166 46.8801 22.0102 46.8519 22.0102C43.968 21.8604 42.0097 20.0575 42.0097 16.9296V3.65561L47.1741 2.56489V6.48472H49.7568V11.6153L49.7559 11.6145Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
<svg
fill="none"
height="19"
viewBox="0 0 119 19"
width="119"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M64.965 8.55517C64.965 8.55517 63.8656 7.85669 63.1858 7.58642C63.1858 7.58642 61.0815 6.58434 60.664 6.10757C60.664 6.10757 59.8439 5.31343 60.3241 4.39105C60.3241 4.39105 60.5251 3.78749 61.3918 3.78749C61.3918 3.78749 62.5201 3.85125 62.5201 4.80406V6.14814H66.5637L66.5567 4.16861C66.5567 4.16861 66.8676 0.926878 61.7938 0.784137C61.7938 0.784137 57.8031 0.514593 56.6585 2.72309C56.6585 2.72309 56.2093 3.2158 56.2093 4.93159V6.17133C56.2093 6.17133 56.1635 7.60164 56.8743 8.52329C56.8743 8.52329 57.2763 9.12758 58.2664 9.79419C58.2664 9.79419 60.2769 10.9064 61.4835 11.5433C61.4835 11.5433 62.7013 12.2577 62.5413 13.3946C62.5413 13.3946 62.4418 14.5619 61.2656 14.5141C61.2656 14.5141 60.1901 14.4648 60.1901 13.32V11.9766H55.8814V13.9279C55.8814 13.9279 55.7608 17.6594 61.2973 17.6594C61.2973 17.6594 66.5877 17.7696 66.8345 13.8301V12.2411C66.8338 12.2411 67.0214 9.8268 64.965 8.55517ZM44.2633 1.24859L42.9093 9.96664H42.5927L41.2994 1.32684H34.545L34.2065 17.1145H38.2057L38.2536 5.21924H38.5681L40.688 17.1124H44.8952L46.9889 5.22359H47.2674L47.3641 17.1145H51.3844L50.9514 1.24859H44.2633ZM20.4573 1.31453L17.7966 17.0964H22.1018L23.678 4.85986H24.032L25.6074 17.0964H29.9119L27.2519 1.31453H20.4573ZM110.794 8.3378V10.6659H111.899V13.1649C111.899 14.3865 110.882 14.409 110.882 14.409C109.647 14.409 109.693 13.2482 109.693 13.2482V4.60408C109.693 3.71358 110.79 3.66576 110.79 3.66576C111.842 3.66576 111.849 4.72798 111.849 4.72798V6.04235H115.903C116.035 3.47664 115.537 2.85569 115.537 2.85569C114.532 0.53561 110.728 0.66241 110.728 0.66241C104.883 0.66241 105.531 5.27069 105.531 5.27069V13.6286C105.657 17.9507 111.417 17.5116 111.514 17.508C114.076 17.2167 114.85 16.3798 114.85 16.3798C115.562 15.8364 115.745 15.0836 115.745 15.0836C115.949 14.6452 116 13.2482 116 13.2482V8.3378H110.794ZM97.3582 10.9064H97.1819L93.0685 1.25293H88.1469V17.116H92.1524L91.9127 7.46397H92.0918L96.3533 17.116H101.125V1.25148H97.069L97.3582 10.9064ZM78.3694 13.1635C78.3694 13.1635 78.4272 14.4655 77.2524 14.4655C77.2524 14.4655 76.0168 14.5336 76.0168 13.1961L76.0042 1.26235H71.6108V13.1011C71.6108 13.1011 71.1602 17.5819 77.3144 17.5819C77.3144 17.5819 82.6626 17.6471 82.6626 13.3069V1.26308H78.3694V13.1635ZM12.0817 8.55517C12.0817 8.55517 10.9837 7.85669 10.3032 7.58714C10.3032 7.58714 8.20032 6.58579 7.78285 6.10829C7.78285 6.10829 6.9627 5.31271 7.44365 4.3925C7.44365 4.3925 7.64392 3.78893 8.5099 3.78893C8.5099 3.78893 9.63892 3.85197 9.63892 4.80478V6.14887H13.6839L13.6762 4.16861C13.6762 4.16861 13.985 0.926878 8.91327 0.784861C8.91327 0.784861 8.53106 0.759499 7.97043 0.788482C7.97043 0.788482 4.89789 0.958033 3.7992 2.68976C3.79285 2.7028 3.78368 2.71223 3.77734 2.72382C3.77734 2.72382 3.32883 3.2158 3.32883 4.93231V6.17205C3.32883 6.17205 3.28229 7.60236 3.99383 8.52402C3.99383 8.52402 4.39579 9.12831 5.38589 9.79491C5.38589 9.79491 7.39569 10.9071 8.60228 11.544C8.60228 11.544 9.82156 12.257 9.66008 13.3953C9.66008 13.3953 9.56064 14.5626 8.38508 14.5148C8.38508 14.5148 7.30966 14.4655 7.30966 13.3207V11.9766H3.00021V13.9293C3.00021 13.9293 2.87962 17.6609 8.41611 17.6609C8.41611 17.6609 13.7051 17.771 13.954 13.8315V12.244C13.954 12.2425 14.1388 9.8268 12.0817 8.55517Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
<svg
fill="none"
height="36"
viewBox="0 0 59 36"
width="59"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.7684 12.8869C16.7684 13.6049 16.8461 14.1872 16.9819 14.6142C17.1372 15.0411 17.3313 15.5069 17.603 16.0115C17.7 16.1668 17.7388 16.3221 17.7388 16.4579C17.7388 16.652 17.6224 16.8461 17.3701 17.0401L16.1474 17.8553C15.9727 17.9717 15.798 18.0299 15.6428 18.0299C15.4487 18.0299 15.2546 17.9329 15.0605 17.7582C14.7888 17.4671 14.5559 17.1566 14.3619 16.8461C14.1678 16.5161 13.9737 16.1474 13.7602 15.701C12.2464 17.4865 10.3444 18.3793 8.05429 18.3793C6.42402 18.3793 5.12369 17.9135 4.17271 16.9819C3.22172 16.0503 2.73652 14.8082 2.73652 13.2556C2.73652 11.6059 3.31876 10.2668 4.50264 9.25758C5.68652 8.24837 7.25856 7.74376 9.25758 7.74376C9.91744 7.74376 10.5967 7.80199 11.3148 7.89903C12.0329 7.99607 12.7704 8.15133 13.5467 8.326V6.90922C13.5467 5.43422 13.2362 4.40561 12.6345 3.80396C12.0135 3.20232 10.9655 2.9112 9.47106 2.9112C8.79179 2.9112 8.0931 2.98883 7.37501 3.1635C6.65692 3.33817 5.95823 3.55166 5.27896 3.82337C4.96843 3.95922 4.73554 4.03686 4.59968 4.07567C4.46383 4.11449 4.36679 4.1339 4.28916 4.1339C4.01744 4.1339 3.88159 3.93982 3.88159 3.53225V2.58126C3.88159 2.27074 3.9204 2.03784 4.01744 1.90199C4.11448 1.76613 4.28915 1.63028 4.56087 1.49442C5.24014 1.14508 6.05527 0.853961 7.00626 0.621067C7.95725 0.368764 8.96646 0.252317 10.0339 0.252317C12.3434 0.252317 14.0319 0.77633 15.1188 1.82436C16.1862 2.87238 16.7296 4.46383 16.7296 6.5987V12.8869H16.7684ZM8.88883 15.8369C9.52929 15.8369 10.1892 15.7204 10.8878 15.4875C11.5865 15.2546 12.2076 14.8276 12.7316 14.2454C13.0421 13.8767 13.275 13.4691 13.3915 13.0033C13.5079 12.5375 13.5855 11.9747 13.5855 11.3148V10.4997C13.0227 10.3638 12.4211 10.2474 11.8 10.1698C11.179 10.0921 10.5773 10.0533 9.97567 10.0533C8.67534 10.0533 7.72435 10.3056 7.08389 10.8296C6.44343 11.3536 6.13291 12.0911 6.13291 13.0615C6.13291 13.9737 6.3658 14.653 6.851 15.1188C7.31679 15.604 7.99606 15.8369 8.88883 15.8369ZM24.4734 17.9329C24.124 17.9329 23.8911 17.8747 23.7359 17.7388C23.5806 17.6224 23.4447 17.3507 23.3283 16.9819L18.7674 1.97962C18.651 1.59146 18.5928 1.33916 18.5928 1.2033C18.5928 0.892777 18.748 0.718106 19.0586 0.718106H20.9605C21.3293 0.718106 21.5816 0.77633 21.7174 0.912185C21.8727 1.02863 21.9892 1.30034 22.1056 1.66909L25.3661 14.5171L28.3938 1.66909C28.4908 1.28094 28.6072 1.02863 28.7625 0.912185C28.9178 0.795738 29.1895 0.718106 29.5388 0.718106H31.0915C31.4602 0.718106 31.7125 0.77633 31.8678 0.912185C32.023 1.02863 32.1589 1.30034 32.2365 1.66909L35.303 14.6724L38.6605 1.66909C38.777 1.28094 38.9128 1.02863 39.0487 0.912185C39.204 0.795738 39.4563 0.718106 39.8056 0.718106H41.6105C41.9211 0.718106 42.0957 0.873369 42.0957 1.2033C42.0957 1.30034 42.0763 1.39738 42.0569 1.51383C42.0375 1.63028 41.9987 1.78554 41.9211 1.99903L37.2438 17.0013C37.1273 17.3895 36.9915 17.6418 36.8362 17.7582C36.6809 17.8747 36.4286 17.9523 36.0987 17.9523H34.4296C34.0609 17.9523 33.8086 17.8941 33.6533 17.7582C33.498 17.6224 33.3622 17.3701 33.2845 16.9819L30.2763 4.46383L27.2875 16.9625C27.1905 17.3507 27.074 17.603 26.9188 17.7388C26.7635 17.8747 26.4918 17.9329 26.1424 17.9329H24.4734ZM49.4125 18.4569C48.4033 18.4569 47.3941 18.3405 46.4237 18.1076C45.4533 17.8747 44.6964 17.6224 44.1918 17.3313C43.8813 17.1566 43.6678 16.9625 43.5901 16.7878C43.5125 16.6132 43.4737 16.4191 43.4737 16.2444V15.2546C43.4737 14.8471 43.629 14.653 43.9201 14.653C44.0365 14.653 44.153 14.6724 44.2694 14.7112C44.3859 14.75 44.5605 14.8276 44.7546 14.9053C45.4145 15.1964 46.1326 15.4293 46.8895 15.5846C47.6658 15.7398 48.4227 15.8174 49.199 15.8174C50.4217 15.8174 51.3727 15.604 52.0326 15.177C52.6924 14.75 53.0418 14.129 53.0418 13.3332C53.0418 12.7898 52.8671 12.3434 52.5178 11.9747C52.1684 11.6059 51.5086 11.276 50.5576 10.9655L47.7434 10.0921C46.3267 9.64574 45.2786 8.98587 44.6382 8.11251C43.9977 7.25857 43.6678 6.30758 43.6678 5.29837C43.6678 4.48324 43.8424 3.76515 44.1918 3.14409C44.5411 2.52304 45.0069 1.97962 45.5891 1.55265C46.1714 1.10626 46.8313 0.77633 47.6076 0.543435C48.3839 0.31054 49.199 0.213501 50.053 0.213501C50.4799 0.213501 50.9263 0.232909 51.3533 0.291133C51.7997 0.349356 52.2072 0.426988 52.6148 0.504619C53.003 0.601659 53.3717 0.698698 53.7211 0.815146C54.0704 0.931593 54.3421 1.04804 54.5362 1.16449C54.8079 1.31975 55.002 1.47501 55.1184 1.64969C55.2349 1.80495 55.2931 2.01844 55.2931 2.29015V3.20232C55.2931 3.60988 55.1378 3.82337 54.8467 3.82337C54.6915 3.82337 54.4391 3.74574 54.1092 3.59047C53.003 3.08587 51.7609 2.83357 50.3829 2.83357C49.2767 2.83357 48.4033 3.00824 47.8017 3.37699C47.2 3.74574 46.8895 4.30857 46.8895 5.10429C46.8895 5.64771 47.0836 6.1135 47.4717 6.48225C47.8599 6.851 48.578 7.21975 49.6066 7.54969L52.3625 8.42304C53.7599 8.86942 54.7691 9.49047 55.3707 10.2862C55.9724 11.0819 56.2635 11.9941 56.2635 13.0033C56.2635 13.8378 56.0888 14.5948 55.7589 15.2546C55.4095 15.9145 54.9438 16.4967 54.3421 16.9625C53.7405 17.4477 53.0224 17.7971 52.1878 18.0494C51.3145 18.3211 50.4023 18.4569 49.4125 18.4569Z"
fill="currentColor"
/>
<path
clipRule="evenodd"
d="M53.0806 27.8892C46.6954 32.6053 37.4184 35.1089 29.4418 35.1089C18.2628 35.1089 8.19014 30.975 0.582247 24.1046C-0.0193978 23.5612 0.524023 22.8237 1.24212 23.2507C9.47106 28.025 19.6214 30.9168 30.1211 30.9168C37.2049 30.9168 44.9875 29.4418 52.149 26.4142C53.2165 25.929 54.1286 27.1128 53.0806 27.8892Z"
fill="currentColor"
fillRule="evenodd"
/>
<path
clipRule="evenodd"
d="M55.7395 24.8615C54.9244 23.8135 50.3441 24.3569 48.2674 24.6092C47.6464 24.6869 47.5494 24.1434 48.1122 23.7359C51.7609 21.174 57.7579 21.9115 58.4566 22.7655C59.1553 23.6388 58.2625 29.6359 54.8467 32.5082C54.3227 32.9546 53.8181 32.7217 54.051 32.1395C54.8273 30.2181 56.5546 25.8901 55.7395 24.8615Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
<svg
fill="none"
height="18"
viewBox="0 0 91 18"
width="91"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M9.31948 0.161179C14.2843 0.641702 19.7297 3.12384 22.8531 6.72777C26.8569 11.2922 25.496 15.8571 19.8098 16.8977C14.1241 18.0189 6.27616 15.1364 2.27237 10.5714C-0.77095 7.04755 -0.690299 3.52428 2.11219 1.60275L17.8877 13.2944L9.31948 0.161179Z"
fill="currentColor"
fillRule="evenodd"
/>
<path
clipRule="evenodd"
d="M87.4761 16.8977L85.4745 8.88956C85.2331 7.92908 85.0735 6.96803 84.9139 6.08707H84.8332C84.6736 7.04812 84.593 7.92908 84.3527 8.96965L82.35 16.8977H78.9068L83.4718 0.24127H86.1947L91 16.8977H87.4761ZM34.9445 0.24127L36.9467 8.24887C37.2671 9.20991 37.3472 10.1715 37.5073 11.0514H37.5874C37.7476 10.0909 37.8277 9.12926 38.0679 8.16878L40.0701 0.24127H43.5133L38.9489 16.8977H36.2259L31.4213 0.24127H34.9445ZM45.9949 0.24127H49.2785V16.8977H45.9949V0.24127ZM67.8576 0.24127L69.5383 10.0914L70.8998 0.24127H75.304L76.9858 16.8977H73.6227L72.8219 4.88577L72.2612 8.96965L70.8998 16.8977H68.3375L66.7358 8.88956L66.1752 5.20612V4.88577H66.0951L65.6146 16.8977H62.0907L63.5329 0.24127H67.8576ZM57.4468 0.24127C56.8862 0.641706 56.4863 1.36193 56.166 2.08271C55.285 4.08489 56.2455 5.68607 57.0464 7.12764C57.127 7.28782 57.2877 7.52808 57.3667 7.76834C59.2087 10.8111 59.8494 13.053 58.5686 16.2559L58.2482 16.8972H54.2439C54.7244 16.4962 55.2044 15.9367 55.4446 15.2954C56.4062 13.1331 55.285 11.1314 54.2439 9.1287C53.0431 6.80673 51.7612 4.40468 53.1226 1.1211C53.2033 0.800751 53.5237 0.240143 53.5237 0.240143H57.4468V0.24127Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
</Stack>
</Stack>
</Box>
</Box>
<Box
sx={{
height: '100vh',
boxShadow: 'var(--mui-shadows-8)',
display: 'flex',
flexDirection: 'column',
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
flex: '1 1 auto',
justifyContent: 'center',
p: 3,
}}
>
<Box sx={{ maxWidth: '420px', width: '100%' }}>{children}</Box>
</Box>
</Box>
</Box>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -1,6 +1,6 @@
import { dayjs } from '@/lib/dayjs';
import { CreateForm, LessonCategory } from './type';
import type { CreateForm, LessonCategory } from './type';
// import type { CreateForm, LessonCategory } from '../lp_categories/type';

View File

@@ -26,7 +26,7 @@ import { Option } from '@/components/core/option';
// import { LessonCategory } from '../lp_categories/type';
import { useLessonCategoriesSelection } from './lesson-categories-selection-context';
import { LessonCategory } from './type';
import type { LessonCategory } from './type';
export interface Filters {
email?: string;

View File

@@ -6,7 +6,7 @@ import * as React from 'react';
import { useSelection } from '@/hooks/use-selection';
import type { Selection } from '@/hooks/use-selection';
import { LessonCategory } from './type';
import type { LessonCategory } from './type';
// import type { LessonCategory } from '../lp_categories/type';

View File

@@ -99,7 +99,7 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonC
},
{
formatter: (row): React.JSX.Element => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const mapping = {
active: {

View File

@@ -1,6 +1,7 @@
import { dayjs } from '@/lib/dayjs';
import { CrCategory, CreateFormProps } from './type';
import type { CrCategory} from './type';
import { CreateFormProps } from './type';
export const defaultCrCategory: CrCategory = {
isEmpty: false,

View File

@@ -6,7 +6,7 @@ import * as React from 'react';
import { useSelection } from '@/hooks/use-selection';
import type { Selection } from '@/hooks/use-selection';
import { CrCategory } from './type';
import type { CrCategory } from './type';
function noop(): void {
return undefined;

View File

@@ -97,7 +97,7 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<CrCateg
},
{
formatter: (row): React.JSX.Element => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const mapping = {
active: {

View File

@@ -1,6 +1,7 @@
import { dayjs } from '@/lib/dayjs';
import { CreateFormProps, CrQuestion } from './type';
import type { CrQuestion } from './type';
import { CreateFormProps } from './type';
export const defaultCrQuestion: CrQuestion = {
isEmpty: false,

View File

@@ -24,7 +24,7 @@ import { FilterButton, FilterPopover, useFilterContext } from '@/components/core
import { Option } from '@/components/core/option';
import { useCrQuestionsSelection } from './cr-questions-selection-context';
import { CrQuestion } from './type';
import type { CrQuestion } from './type';
export interface Filters {
email?: string;

View File

@@ -97,7 +97,7 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<CrQuest
},
{
formatter: (row): React.JSX.Element => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const mapping = {
active: {

View File

@@ -89,7 +89,7 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<Custome
{
formatter: (row): React.JSX.Element => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const mapping = {
active: {

View File

@@ -81,7 +81,7 @@ function ErrorDisplay({ message, code, details, severity = 'error' }: PropsError
{formattedMessage}
</Typography>
{details && <ErrorDetails details={details} />}
{details ? <ErrorDetails details={details} /> : null}
</Alert>
</Box>
);

View File

@@ -133,7 +133,7 @@ export function NotificationsPopover({
<Typography
color="lightgray"
variant={'subtitle2'}
variant="subtitle2"
>
{t('list-is-empty')}
</Typography>

Some files were not shown because too many files have changed in this diff Show More