Compare commits
11 Commits
develop/mo
...
13c3399a6e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
13c3399a6e | ||
![]() |
80a2636f90 | ||
![]() |
9c4637528c | ||
![]() |
661de6e8d7 | ||
![]() |
4a0ae590b0 | ||
![]() |
53162ed333 | ||
![]() |
79c292d943 | ||
![]() |
10a6375347 | ||
![]() |
f950617372 | ||
![]() |
99fafda624 | ||
![]() |
3ed3f2fecb |
@@ -1,4 +1,13 @@
|
||||
import type { User } from '@prisma/client';
|
||||
// src/app/api/auth/me/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// - T.B.A.
|
||||
//
|
||||
// RULES:
|
||||
// - T.B.A.
|
||||
//
|
||||
|
||||
import type { PartyUser, User } from '@prisma/client';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { headers } from 'next/headers';
|
||||
@@ -11,9 +20,11 @@ import { getUserById } from 'src/app/services/user.service';
|
||||
import { createAccessLog } from 'src/app/services/access-log.service';
|
||||
|
||||
import { flattenNextjsRequest } from '../sign-in/flattenNextjsRequest';
|
||||
import { getPartyUserById } from 'src/app/services/party-user.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// NOTE: keep this comment to let prisma running on nextjs
|
||||
// export const runtime = 'edge';
|
||||
|
||||
/**
|
||||
@@ -29,6 +40,7 @@ const INVALID_AUTH_TOKEN = 'Invalid authorization token';
|
||||
const USER_ID_NOT_FOUND = 'userId not found';
|
||||
const USER_TOKEN_OK = 'user token check ok';
|
||||
const AUTHORIZATION_TOKEN_MISSING_OR_INVALID = 'Authorization token missing or invalid';
|
||||
const USER_BANNED = 'user banned';
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const debug = { 'req.headers': flattenNextjsRequest(req) };
|
||||
@@ -43,12 +55,17 @@ export async function GET(req: NextRequest) {
|
||||
|
||||
const accessToken = `${authorization}`.split(' ')[1];
|
||||
const data = await verify(accessToken, JWT_SECRET);
|
||||
console.log(data.userId);
|
||||
|
||||
if (data.userId) {
|
||||
// TODO: remove me
|
||||
// const currentUser = _users.find((user) => user.id === data.userId);
|
||||
const currentUser: User | null = await getUserById(data.userId);
|
||||
const { userId } = data;
|
||||
|
||||
let currentUser: User | PartyUser | null = null;
|
||||
|
||||
currentUser = await getPartyUserById(userId);
|
||||
|
||||
if (!currentUser) {
|
||||
currentUser = await getUserById(userId);
|
||||
}
|
||||
|
||||
if (!currentUser) {
|
||||
createAccessLog('', USER_TOKEN_CHECK_FAILED, debug);
|
||||
|
@@ -1,11 +1,26 @@
|
||||
###
|
||||
|
||||
# username and password ok
|
||||
GET http://localhost:7272/api/auth/me
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWJnbnUyengwMDBjaHEzaGZ3dmtjejlvIiwiaWF0IjoxNzQ4OTY0ODkyLCJleHAiOjE3NTAxNzQ0OTJ9.lo04laCxtm0IVeYaETEV3hXKyDmXPEn7SyWtY2VR4dI
|
||||
|
||||
GET http://localhost:7272/api/auth/me
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWMwdWo4aXgwMDBqM2Y1eWhxc29xMW9wIiwiaWF0IjoxNzUwMjE5NTYyLCJleHAiOjE3NTE0MjkxNjJ9.8gKM2oMquccM_HDEfBAgtapCGf3M1eIp6SZ_knx7d1g
|
||||
|
||||
###
|
||||
|
||||
# username and password ok
|
||||
|
||||
POST http://localhost:7272/api/auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"email": "demo@minimals.cc",
|
||||
"password": "@2Minimal"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# There is no user corresponding to the email address.
|
||||
|
||||
POST http://localhost:7272/api/auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
@@ -15,7 +30,9 @@ content-type: application/json
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Wrong password
|
||||
|
||||
POST http://localhost:7272/api/auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
|
21
03_source/cms_backend/src/app/api/event/helloworld/route.ts
Normal file
21
03_source/cms_backend/src/app/api/event/helloworld/route.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { countTotalEvents } from 'src/app/services/eventItem.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - Events, obsoleted
|
||||
*************************************** */
|
||||
export async function GET() {
|
||||
try {
|
||||
const numOfEvent = await countTotalEvents();
|
||||
|
||||
logger('[Event] list', numOfEvent);
|
||||
|
||||
return response({ numOfEvent }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('Event - Get list', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/event/helloworld
|
22
03_source/cms_backend/src/app/api/event/numOfEvent/route.ts
Normal file
22
03_source/cms_backend/src/app/api/event/numOfEvent/route.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
// src/app/api/event/list/route.ts
|
||||
import { countTotalEvents, listEvents } from 'src/app/services/eventItem.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - Events, obsoleted
|
||||
*************************************** */
|
||||
export async function GET() {
|
||||
try {
|
||||
const numOfEvents = await countTotalEvents();
|
||||
|
||||
// logger('[Event] list', numOfEvents.length);
|
||||
|
||||
return response({ numOfEvents }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('Event - Get list', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/event/numOfEvent
|
@@ -0,0 +1,23 @@
|
||||
//
|
||||
//
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { countTotalEvents } from 'src/app/services/eventItem.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - Events, obsoleted
|
||||
*************************************** */
|
||||
export async function GET() {
|
||||
try {
|
||||
const numOfEvent = await countTotalEvents();
|
||||
|
||||
// logger('[Event] list', numOfEvent);
|
||||
|
||||
return response({ numOfEvent }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('Event - Get list', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/party-event/numOfEvent
|
@@ -7,6 +7,14 @@
|
||||
// - Log all access attempts (success/failure)
|
||||
// - Validate token structure and user existence
|
||||
//
|
||||
// RULES:
|
||||
// - Must validate Bearer token format before processing
|
||||
// - All errors must be logged via access-log service
|
||||
// - User existence must be verified after token validation
|
||||
// - Sensitive data must be filtered from responses
|
||||
// - Mock JWT_SECRET should be replaced in production
|
||||
// - Debug info should be included in error logs
|
||||
//
|
||||
import type { NextRequest } from 'next/server';
|
||||
import type { PartyUser } from '@prisma/client';
|
||||
|
||||
@@ -23,14 +31,6 @@ import { flattenNextjsRequest } from '../sign-in/flattenNextjsRequest';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This API is used for demo purpose only
|
||||
* You should use a real database
|
||||
* You should hash the password before saving to database
|
||||
* You should not save the password in the database
|
||||
* You should not expose the JWT_SECRET in the client side
|
||||
*/
|
||||
|
||||
const ERR_USER_TOKEN_CHECK_FAILED = 'user token check failed';
|
||||
const ERR_INVALID_AUTH_TOKEN = 'Invalid authorization token';
|
||||
const ERR_USER_ID_NOT_FOUND = 'userId not found';
|
||||
|
@@ -21,8 +21,8 @@ POST http://localhost:7272/api/party-user-auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"email": "demo@minimals.cc",
|
||||
"password": "@2Minimal"
|
||||
"email": "party_user0@prisma.io",
|
||||
"password": "Aa12345678"
|
||||
}
|
||||
|
||||
###
|
||||
|
@@ -0,0 +1,6 @@
|
||||
export const ERR_USER_NOT_FOUND = 'There is no user corresponding to the email address.';
|
||||
export const ERR_WRONG_PASSWORD = 'Wrong password';
|
||||
export const LOG_USER_TRIED_LOGIN_WITH_EMAIL = `user tried login with email`;
|
||||
export const LOG_USER_LOGGED_WITH_WRONG_PASSWORD = 'user logged with wrong password';
|
||||
export const LOG_ACCESS_GRANTED = 'access granted';
|
||||
export const LOG_ATTEMPTED_LOGIN_BUT_FAILED = 'attempted login but failed';
|
@@ -10,9 +10,16 @@ import { createAccessLog } from 'src/app/services/access-log.service';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
import { flattenNextjsRequest } from './flattenNextjsRequest';
|
||||
import {
|
||||
LOG_USER_TRIED_LOGIN_WITH_EMAIL,
|
||||
ERR_USER_NOT_FOUND,
|
||||
LOG_USER_LOGGED_WITH_WRONG_PASSWORD,
|
||||
ERR_WRONG_PASSWORD,
|
||||
LOG_ACCESS_GRANTED,
|
||||
LOG_ATTEMPTED_LOGIN_BUT_FAILED,
|
||||
} from './constants';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This API is used for demo purpose only
|
||||
* You should use a real database
|
||||
@@ -21,9 +28,6 @@ import { flattenNextjsRequest } from './flattenNextjsRequest';
|
||||
* You should not expose the JWT_SECRET in the client side
|
||||
*/
|
||||
|
||||
const ERR_USER_NOT_FOUND = 'There is no user corresponding to the email address.';
|
||||
const ERR_WRONG_PASSWORD = 'Wrong password';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const debug = { 'req.headers': flattenNextjsRequest(req) };
|
||||
|
||||
@@ -32,12 +36,12 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
const currentUser = await prisma.partyUser.findFirst({ where: { email } });
|
||||
if (!currentUser) {
|
||||
await createAccessLog('', `user tried login with email ${email}`, { debug });
|
||||
await createAccessLog('', LOG_USER_TRIED_LOGIN_WITH_EMAIL, { email, debug });
|
||||
return response({ message: ERR_USER_NOT_FOUND }, STATUS.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (currentUser?.password !== password) {
|
||||
await createAccessLog(currentUser.id, 'user logged with wrong password', { debug });
|
||||
await createAccessLog(currentUser.id, LOG_USER_LOGGED_WITH_WRONG_PASSWORD, { debug });
|
||||
return response({ message: ERR_WRONG_PASSWORD }, STATUS.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@@ -45,11 +49,11 @@ export async function POST(req: NextRequest) {
|
||||
expiresIn: JWT_EXPIRES_IN,
|
||||
});
|
||||
|
||||
await createAccessLog(currentUser.id, 'access granted', { debug });
|
||||
await createAccessLog(currentUser.id, LOG_ACCESS_GRANTED, { debug });
|
||||
|
||||
return response({ user: currentUser, accessToken }, STATUS.OK);
|
||||
} catch (error) {
|
||||
await createAccessLog('', 'attempted login but failed', { debug, error });
|
||||
await createAccessLog('', LOG_ATTEMPTED_LOGIN_BUT_FAILED, { debug, error });
|
||||
|
||||
return handleError('Auth - Sign in', error);
|
||||
}
|
||||
|
@@ -2,14 +2,19 @@
|
||||
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/auth/me
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWMwdWo4bGwwMDByM2Y1eXhob3JuMW1hIiwiaWF0IjoxNzUwMjE5NTgwLCJleHAiOjE3NTE0MjkxODB9.7BtuIKEvwDcHc5j9JYX0Eb1uB37kFH1Ksx4MTDTtEWQ
|
||||
|
||||
###
|
||||
|
||||
# username and password ok
|
||||
|
||||
POST http://localhost:7272/api/party-user-auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"email": "demo@minimals.cc",
|
||||
"password": "@2Minimal"
|
||||
"email": "party_user0@prisma.io",
|
||||
"password": "Aa12345678"
|
||||
}
|
||||
|
||||
###
|
||||
|
@@ -1,28 +1,23 @@
|
||||
// src/app/services/AccessLog.service.ts
|
||||
// src/app/services/access-log.service.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// Service for handling AccessLog records
|
||||
// - Core service for audit logging and access tracking
|
||||
// - Records all authentication attempts and system access
|
||||
// - Provides query capabilities for audit trails
|
||||
// - Integrates with Prisma ORM for database operations
|
||||
//
|
||||
// RULES:
|
||||
// - All methods return Promises
|
||||
// - Input validation should be done at controller level
|
||||
// - Errors should be propagated to caller
|
||||
// - All methods return Promises for async operations
|
||||
// - Input validation must be done at controller level
|
||||
// - Errors should be propagated to caller with context
|
||||
// - Audit records should never be modified after creation
|
||||
// - Sensitive data should be hashed before logging
|
||||
// - Metadata should be stored as JSON for flexibility
|
||||
|
||||
import type { AccessLog } from '@prisma/client';
|
||||
|
||||
import prisma from '../lib/prisma';
|
||||
|
||||
// type CreateAccessLog = {
|
||||
// userId?: string;
|
||||
// message?: string;
|
||||
// metadata?: Record<string, any>;
|
||||
// };
|
||||
|
||||
// type UpdateAccessLog = {
|
||||
// status?: number;
|
||||
// metadata?: object;
|
||||
// };
|
||||
|
||||
async function listAccessLogs(): Promise<AccessLog[]> {
|
||||
return prisma.accessLog.findMany({
|
||||
orderBy: { timestamp: 'desc' },
|
||||
|
@@ -51,6 +51,17 @@ async function getEventItemById(eventId: string): Promise<EventItem | null> {
|
||||
return prisma.eventItem.findFirst({ where: { id: eventId } });
|
||||
}
|
||||
|
||||
async function countTotalEvents(): Promise<number> {
|
||||
try {
|
||||
const result = await prisma.eventItem.findMany();
|
||||
console.log({ result });
|
||||
return result.length;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// async function createNewEvent(createForm: CreateEvent) {
|
||||
// return prisma.event.create({ data: createForm });
|
||||
// }
|
||||
@@ -73,4 +84,5 @@ export {
|
||||
// deleteEvent,
|
||||
// createNewEvent,
|
||||
getEventItemById,
|
||||
countTotalEvents,
|
||||
};
|
||||
|
@@ -23,7 +23,7 @@
|
||||
"re:build-npm": "npm run clean && npm install && npm run build",
|
||||
"tsc:dev": "yarn dev & yarn tsc:watch",
|
||||
"tsc:print": "npx tsc --showConfig",
|
||||
"tsc:w": "npx nodemon --delay 1 --ext ts,tsx --exec \"yarn tsc\"",
|
||||
"tsc:w": "npx nodemon --delay 1 --ext ts,tsx --exec \"yarn tsc || (sleep 10; touch src/app.tsx)\"",
|
||||
"tsc:watch": "tsc --noEmit --watch",
|
||||
"tsc": "tsc --noEmit"
|
||||
},
|
||||
@@ -147,4 +147,4 @@
|
||||
"vite": "^6.2.3",
|
||||
"vite-plugin-checker": "^0.9.1"
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import { endpoints, fetcher } from 'src/lib/axios';
|
||||
import { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IPostItem } from 'src/types/blog';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR from 'swr';
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import axios, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axios, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { ICalendarEvent } from 'src/types/calendar';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { keyBy } from 'es-toolkit';
|
||||
import { useMemo } from 'react';
|
||||
import axios, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axios, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IChatConversation, IChatMessage, IChatParticipant } from 'src/types/chat';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
// src/actions/invoice.ts
|
||||
import { useMemo } from 'react';
|
||||
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IInvoiceItem, SaveInvoiceData } from 'src/types/invoice';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR from 'swr';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import type { UniqueIdentifier } from '@dnd-kit/core';
|
||||
import { startTransition, useMemo } from 'react';
|
||||
import axios, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axios, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IKanban, IKanbanColumn, IKanbanTask } from 'src/types/kanban';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { keyBy } from 'es-toolkit';
|
||||
import { useMemo } from 'react';
|
||||
import { endpoints, fetcher } from 'src/lib/axios';
|
||||
import { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IMail, IMailLabel } from 'src/types/mail';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR from 'swr';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
// src/actions/order.ts
|
||||
import { useMemo } from 'react';
|
||||
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IOrderItem } from 'src/types/order';
|
||||
import type { IProductItem } from 'src/types/product';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
|
@@ -1,7 +1,8 @@
|
||||
// src/actions/party-event.ts
|
||||
//
|
||||
import { useMemo } from 'react';
|
||||
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IPartyEventItem } from 'src/types/party-event';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
|
@@ -1,7 +1,8 @@
|
||||
// src/actions/party-order.ts
|
||||
//
|
||||
import { useMemo } from 'react';
|
||||
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IPartyOrderItem } from 'src/types/party-order';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
|
@@ -1,7 +1,8 @@
|
||||
// src/actions/party-user1.ts
|
||||
//
|
||||
import { useMemo } from 'react';
|
||||
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IPartyUserItem } from 'src/types/party-user';
|
||||
import type { IProductItem } from 'src/types/product';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
|
@@ -1,7 +1,8 @@
|
||||
// src/actions/product.ts
|
||||
//
|
||||
import { useMemo } from 'react';
|
||||
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IProductItem } from 'src/types/product';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { IProductItem } from 'src/types/product';
|
||||
import { IUserItem } from 'src/types/user';
|
||||
import type { SWRConfiguration } from 'swr';
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import axios, { endpoints } from 'src/lib/axios';
|
||||
import axios from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import { JWT_STORAGE_KEY } from './constant';
|
||||
import { setSession } from './utils';
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { useSetState } from 'minimal-shared/hooks';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import axios, { endpoints } from 'src/lib/axios';
|
||||
import axios from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import type { AuthState } from '../../types';
|
||||
import { AuthContext } from '../auth-context';
|
||||
import { JWT_STORAGE_KEY } from './constant';
|
||||
|
@@ -0,0 +1,2 @@
|
||||
export const ERR_ACCESS_TOKEN_NOT_FOUND = `Access token not found in response`;
|
||||
export const ACCESS_TOKEN_NOT_FOUND_IN_RESPONSE = 'Access token not found in response';
|
85
03_source/frontend/src/auth/context/party-user-jwt/action.ts
Normal file
85
03_source/frontend/src/auth/context/party-user-jwt/action.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import axios from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import { JWT_STORAGE_KEY } from './constant';
|
||||
import { ACCESS_TOKEN_NOT_FOUND_IN_RESPONSE, ERR_ACCESS_TOKEN_NOT_FOUND } from './ERRORS';
|
||||
import { setSession } from './utils';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export type SignInParams = {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
export type SignUpParams = {
|
||||
email: string;
|
||||
password: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
};
|
||||
|
||||
/** **************************************
|
||||
* Sign in
|
||||
*************************************** */
|
||||
export const signInWithPassword = async ({ email, password }: SignInParams): Promise<void> => {
|
||||
try {
|
||||
const params = { email, password };
|
||||
|
||||
const res = await axios.post(endpoints.partyUserAuth.signIn, params);
|
||||
|
||||
const { accessToken } = res.data;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error(ERR_ACCESS_TOKEN_NOT_FOUND);
|
||||
}
|
||||
|
||||
setSession(accessToken);
|
||||
} catch (error) {
|
||||
console.error('Error during sign in:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/** **************************************
|
||||
* Sign up
|
||||
*************************************** */
|
||||
export const signUp = async ({
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
}: SignUpParams): Promise<void> => {
|
||||
const params = {
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await axios.post(endpoints.auth.signUp, params);
|
||||
|
||||
const { accessToken } = res.data;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error(ACCESS_TOKEN_NOT_FOUND_IN_RESPONSE);
|
||||
}
|
||||
|
||||
sessionStorage.setItem(JWT_STORAGE_KEY, accessToken);
|
||||
} catch (error) {
|
||||
console.error('Error during sign up:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/** **************************************
|
||||
* Sign out
|
||||
*************************************** */
|
||||
export const signOut = async (): Promise<void> => {
|
||||
try {
|
||||
await setSession(null);
|
||||
} catch (error) {
|
||||
console.error('Error during sign out:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
@@ -0,0 +1 @@
|
||||
export const JWT_STORAGE_KEY = 'jwt_access_token';
|
@@ -0,0 +1,7 @@
|
||||
export * from './utils';
|
||||
|
||||
export * from './action';
|
||||
|
||||
export * from './constant';
|
||||
|
||||
// export * from './auth-provider';
|
94
03_source/frontend/src/auth/context/party-user-jwt/utils.ts
Normal file
94
03_source/frontend/src/auth/context/party-user-jwt/utils.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import axios from 'src/lib/axios';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import { JWT_STORAGE_KEY } from './constant';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function jwtDecode(token: string) {
|
||||
try {
|
||||
if (!token) return null;
|
||||
|
||||
const parts = token.split('.');
|
||||
if (parts.length < 2) {
|
||||
throw new Error('Invalid token!');
|
||||
}
|
||||
|
||||
const base64Url = parts[1];
|
||||
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
||||
const decoded = JSON.parse(atob(base64));
|
||||
|
||||
return decoded;
|
||||
} catch (error) {
|
||||
console.error('Error decoding token:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function isValidToken(accessToken: string) {
|
||||
if (!accessToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwtDecode(accessToken);
|
||||
|
||||
if (!decoded || !('exp' in decoded)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentTime = Date.now() / 1000;
|
||||
|
||||
return decoded.exp > currentTime;
|
||||
} catch (error) {
|
||||
console.error('Error during token validation:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function tokenExpired(exp: number) {
|
||||
const currentTime = Date.now();
|
||||
const timeLeft = exp * 1000 - currentTime;
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
alert('Token expired!');
|
||||
sessionStorage.removeItem(JWT_STORAGE_KEY);
|
||||
window.location.href = paths.auth.jwt.signIn;
|
||||
} catch (error) {
|
||||
console.error('Error during token expiration:', error);
|
||||
throw error;
|
||||
}
|
||||
}, timeLeft);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const INVALID_ACCESS_TOKEN = 'Invalid access token!';
|
||||
|
||||
export async function setSession(accessToken: string | null) {
|
||||
try {
|
||||
if (accessToken) {
|
||||
sessionStorage.setItem(JWT_STORAGE_KEY, accessToken);
|
||||
|
||||
axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
|
||||
|
||||
const decodedToken = jwtDecode(accessToken); // ~3 days by minimals server
|
||||
|
||||
if (decodedToken && 'exp' in decodedToken) {
|
||||
tokenExpired(decodedToken.exp);
|
||||
} else {
|
||||
throw new Error(INVALID_ACCESS_TOKEN);
|
||||
}
|
||||
} else {
|
||||
sessionStorage.removeItem(JWT_STORAGE_KEY);
|
||||
delete axios.defaults.headers.common.Authorization;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during set session:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
3
03_source/frontend/src/auth/view/party-user-jwt/index.ts
Normal file
3
03_source/frontend/src/auth/view/party-user-jwt/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './jwt-sign-in-view';
|
||||
|
||||
export * from './jwt-sign-up-view';
|
@@ -0,0 +1,167 @@
|
||||
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 IconButton from '@mui/material/IconButton';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import Link from '@mui/material/Link';
|
||||
import { useBoolean } from 'minimal-shared/hooks';
|
||||
import React, { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Field, Form } from 'src/components/hook-form';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { RouterLink } from 'src/routes/components';
|
||||
import { useRouter } from 'src/routes/hooks';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import { z as zod } from 'zod';
|
||||
import { FormHead } from '../../components/form-head';
|
||||
import { signInWithPassword } from '../../context/party-user-jwt';
|
||||
import { useAuthContext } from '../../hooks';
|
||||
import { getErrorMessage } from '../../utils';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function JwtSignInView(): React.JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const showPassword = useBoolean();
|
||||
|
||||
const { checkUserSession } = useAuthContext();
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
|
||||
type SignInSchemaType = zod.infer<typeof SignInSchema>;
|
||||
|
||||
const EMAIL_IS_REQUIRED = 'Email is required!';
|
||||
const EMAIL_MUST_BE_A_VALID_EMAIL_ADDRESS = 'Email must be a valid email address!';
|
||||
const PASSWORD_IS_REQUIRED = 'Password is required!';
|
||||
const PASSWORD_MUST_BE_AT_LEAST_6_CHARACTERS = 'Password must be at least 6 characters!';
|
||||
|
||||
const SignInSchema = zod.object({
|
||||
email: zod
|
||||
.string()
|
||||
.min(1, { message: EMAIL_IS_REQUIRED })
|
||||
.email({ message: EMAIL_MUST_BE_A_VALID_EMAIL_ADDRESS }),
|
||||
password: zod
|
||||
.string()
|
||||
.min(1, { message: PASSWORD_IS_REQUIRED })
|
||||
.min(6, { message: PASSWORD_MUST_BE_AT_LEAST_6_CHARACTERS }),
|
||||
});
|
||||
|
||||
const defaultValues: SignInSchemaType = {
|
||||
email: 'party_user0@prisma.io',
|
||||
password: 'Aa12345678',
|
||||
};
|
||||
|
||||
const methods = useForm<SignInSchemaType>({
|
||||
resolver: zodResolver(SignInSchema),
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting },
|
||||
} = methods;
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
try {
|
||||
await signInWithPassword({ email: data.email, password: data.password });
|
||||
await checkUserSession?.();
|
||||
|
||||
router.refresh();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const feedbackMessage = getErrorMessage(error);
|
||||
setErrorMessage(feedbackMessage);
|
||||
}
|
||||
});
|
||||
|
||||
const renderForm = () => (
|
||||
<Box sx={{ gap: 3, display: 'flex', flexDirection: 'column' }}>
|
||||
<Field.Text name="email" label="Email address" slotProps={{ inputLabel: { shrink: true } }} />
|
||||
|
||||
<Box sx={{ gap: 1.5, display: 'flex', flexDirection: 'column' }}>
|
||||
<Link
|
||||
component={RouterLink}
|
||||
href="#"
|
||||
variant="body2"
|
||||
color="inherit"
|
||||
sx={{ alignSelf: 'flex-end' }}
|
||||
>
|
||||
Forgot password?
|
||||
</Link>
|
||||
|
||||
<Field.Text
|
||||
name="password"
|
||||
label="Password"
|
||||
placeholder="6+ characters"
|
||||
type={showPassword.value ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
inputLabel: { shrink: true },
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={showPassword.onToggle} edge="end">
|
||||
<Iconify
|
||||
icon={showPassword.value ? 'solar:eye-bold' : 'solar:eye-closed-bold'}
|
||||
/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
fullWidth
|
||||
color="inherit"
|
||||
size="large"
|
||||
type="submit"
|
||||
variant="contained"
|
||||
loading={isSubmitting}
|
||||
loadingIndicator="Sign in..."
|
||||
>
|
||||
{t('sign-in')}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormHead
|
||||
title="Sign in to your account"
|
||||
description={
|
||||
<>
|
||||
{`Don’t have an account? `}
|
||||
<Link component={RouterLink} href={paths.partyUserAuth.jwt.signUp} variant="subtitle2">
|
||||
{t('get-started')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
sx={{ textAlign: { xs: 'center', md: 'left' } }}
|
||||
/>
|
||||
|
||||
<Alert severity="info" sx={{ mb: 3 }}>
|
||||
Use <strong>{defaultValues.email}</strong>
|
||||
{' with password '}
|
||||
<strong>{defaultValues.password}</strong>
|
||||
</Alert>
|
||||
|
||||
{!!errorMessage && (
|
||||
<Alert severity="error" sx={{ mb: 3 }}>
|
||||
{errorMessage}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Form methods={methods} onSubmit={onSubmit}>
|
||||
{renderForm()}
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,166 @@
|
||||
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 IconButton from '@mui/material/IconButton';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import Link from '@mui/material/Link';
|
||||
import { useBoolean } from 'minimal-shared/hooks';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { Field, Form } from 'src/components/hook-form';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { RouterLink } from 'src/routes/components';
|
||||
import { useRouter } from 'src/routes/hooks';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import { z as zod } from 'zod';
|
||||
import { FormHead } from '../../components/form-head';
|
||||
import { SignUpTerms } from '../../components/sign-up-terms';
|
||||
import { signUp } from '../../context/jwt';
|
||||
import { useAuthContext } from '../../hooks';
|
||||
import { getErrorMessage } from '../../utils';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export type SignUpSchemaType = zod.infer<typeof SignUpSchema>;
|
||||
|
||||
export const SignUpSchema = 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({ message: 'Email must be a valid email address!' }),
|
||||
password: zod
|
||||
.string()
|
||||
.min(1, { message: 'Password is required!' })
|
||||
.min(6, { message: 'Password must be at least 6 characters!' }),
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function JwtSignUpView() {
|
||||
const router = useRouter();
|
||||
|
||||
const showPassword = useBoolean();
|
||||
|
||||
const { checkUserSession } = useAuthContext();
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
|
||||
const defaultValues: SignUpSchemaType = {
|
||||
firstName: 'Hello',
|
||||
lastName: 'Friend',
|
||||
email: 'hello@gmail.com',
|
||||
password: '@2Minimal',
|
||||
};
|
||||
|
||||
const methods = useForm<SignUpSchemaType>({
|
||||
resolver: zodResolver(SignUpSchema),
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting },
|
||||
} = methods;
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
try {
|
||||
await signUp({
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
});
|
||||
await checkUserSession?.();
|
||||
|
||||
router.refresh();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const feedbackMessage = getErrorMessage(error);
|
||||
setErrorMessage(feedbackMessage);
|
||||
}
|
||||
});
|
||||
|
||||
const renderForm = () => (
|
||||
<Box sx={{ gap: 3, display: 'flex', flexDirection: 'column' }}>
|
||||
<Box
|
||||
sx={{ display: 'flex', gap: { xs: 3, sm: 2 }, flexDirection: { xs: 'column', sm: 'row' } }}
|
||||
>
|
||||
<Field.Text
|
||||
name="firstName"
|
||||
label="First name"
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
/>
|
||||
<Field.Text
|
||||
name="lastName"
|
||||
label="Last name"
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Field.Text name="email" label="Email address" slotProps={{ inputLabel: { shrink: true } }} />
|
||||
|
||||
<Field.Text
|
||||
name="password"
|
||||
label="Password"
|
||||
placeholder="6+ characters"
|
||||
type={showPassword.value ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
inputLabel: { shrink: true },
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={showPassword.onToggle} edge="end">
|
||||
<Iconify icon={showPassword.value ? 'solar:eye-bold' : 'solar:eye-closed-bold'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
fullWidth
|
||||
color="inherit"
|
||||
size="large"
|
||||
type="submit"
|
||||
variant="contained"
|
||||
loading={isSubmitting}
|
||||
loadingIndicator="Create account..."
|
||||
>
|
||||
Create account
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormHead
|
||||
title="Hi New party user, Get started absolutely free "
|
||||
description={
|
||||
<>
|
||||
{`Already have an account? `}
|
||||
<Link component={RouterLink} href={paths.auth.jwt.signIn} variant="subtitle2">
|
||||
Get started
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
sx={{ textAlign: { xs: 'center', md: 'left' } }}
|
||||
/>
|
||||
|
||||
{!!errorMessage && (
|
||||
<Alert severity="error" sx={{ mb: 3 }}>
|
||||
{errorMessage}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Form methods={methods} onSubmit={onSubmit}>
|
||||
{renderForm()}
|
||||
</Form>
|
||||
|
||||
<SignUpTerms />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -1,3 +1,18 @@
|
||||
// src/lib/axios.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// - Centralized Axios instance configuration
|
||||
// - Global response error handling
|
||||
// - Standardized API endpoint definitions
|
||||
// - Reusable fetcher utility
|
||||
//
|
||||
// RULES:
|
||||
// - All API calls must use this axiosInstance
|
||||
// - Custom error handling in interceptor
|
||||
// - Endpoints should be defined here for consistency
|
||||
// - Fetcher should be used for simple GET requests
|
||||
//
|
||||
|
||||
import type { AxiosRequestConfig } from 'axios';
|
||||
import axios from 'axios';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
@@ -27,94 +42,3 @@ export const fetcher = async (args: string | [string, AxiosRequestConfig]) => {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const endpoints = {
|
||||
chat: '/api/chat',
|
||||
kanban: '/api/kanban',
|
||||
calendar: '/api/calendar',
|
||||
auth: {
|
||||
me: '/api/auth/me',
|
||||
signIn: '/api/auth/sign-in',
|
||||
signUp: '/api/auth/sign-up',
|
||||
},
|
||||
mail: {
|
||||
list: '/api/mail/list',
|
||||
details: '/api/mail/details',
|
||||
labels: '/api/mail/labels',
|
||||
},
|
||||
post: {
|
||||
list: '/api/post/list',
|
||||
details: '/api/post/details',
|
||||
latest: '/api/post/latest',
|
||||
search: '/api/post/search',
|
||||
},
|
||||
product: {
|
||||
list: '/api/product/list',
|
||||
details: '/api/product/details',
|
||||
search: '/api/product/search',
|
||||
save: '/api/product/saveProduct',
|
||||
create: '/api/product/create',
|
||||
update: '/api/product/update',
|
||||
delete: '/api/product/delete',
|
||||
},
|
||||
user: {
|
||||
list: '/api/user/list',
|
||||
profile: '/api/user/profile',
|
||||
update: '/api/user/update',
|
||||
settings: '/api/user/settings',
|
||||
details: '/api/user/details',
|
||||
},
|
||||
order: {
|
||||
list: '/api/order/list',
|
||||
profile: '/api/order/profile',
|
||||
update: '/api/order/update',
|
||||
settings: '/api/order/settings',
|
||||
details: '/api/order/details',
|
||||
changeStatus: (orderId: string) => `/api/order/changeStatus?orderId=${orderId}`,
|
||||
},
|
||||
invoice: {
|
||||
list: '/api/invoice/list',
|
||||
profile: '/api/invoice/profile',
|
||||
update: '/api/invoice/update',
|
||||
saveInvoice: (invoiceId: string) => `/api/invoice/saveInvoice?invoiceId=${invoiceId}`,
|
||||
settings: '/api/invoice/settings',
|
||||
details: '/api/invoice/details',
|
||||
changeStatus: (invoiceId: string) => `/api/invoice/changeStatus?invoiceId=${invoiceId}`,
|
||||
search: '/api/invoice/search',
|
||||
},
|
||||
//
|
||||
//
|
||||
//
|
||||
partyEvent: {
|
||||
list: '/api/party-event/list',
|
||||
details: '/api/party-event/details',
|
||||
search: '/api/party-event/search',
|
||||
create: '/api/party-event/create',
|
||||
update: '/api/party-event/update',
|
||||
delete: '/api/party-event/delete',
|
||||
},
|
||||
partyOrder: {
|
||||
create: '/api/party-order/create',
|
||||
delete: '/api/party-order/delete',
|
||||
list: '/api/party-order/list',
|
||||
profile: '/api/party-order/profile',
|
||||
update: '/api/party-order/update',
|
||||
settings: '/api/party-order/settings',
|
||||
details: '/api/party-order/details',
|
||||
changeStatus: (partyOrderId: string) =>
|
||||
`/api/party-order/changeStatus?partyOrderId=${partyOrderId}`,
|
||||
},
|
||||
partyUser: {
|
||||
list: '/api/party-user/list',
|
||||
details: '/api/party-user/details',
|
||||
search: '/api/party-user/search',
|
||||
create: '/api/party-user/create',
|
||||
update: '/api/party-user/update',
|
||||
delete: '/api/party-user/delete',
|
||||
//
|
||||
detailsByPartyUserId: (partyUserId: string) =>
|
||||
`/api/party-user/details?partyUserId=${partyUserId}`,
|
||||
},
|
||||
};
|
||||
|
96
03_source/frontend/src/lib/endpoints.ts
Normal file
96
03_source/frontend/src/lib/endpoints.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const endpoints = {
|
||||
chat: '/api/chat',
|
||||
kanban: '/api/kanban',
|
||||
calendar: '/api/calendar',
|
||||
auth: {
|
||||
me: '/api/auth/me',
|
||||
signIn: '/api/auth/sign-in',
|
||||
signUp: '/api/auth/sign-up',
|
||||
},
|
||||
mail: {
|
||||
list: '/api/mail/list',
|
||||
details: '/api/mail/details',
|
||||
labels: '/api/mail/labels',
|
||||
},
|
||||
post: {
|
||||
list: '/api/post/list',
|
||||
details: '/api/post/details',
|
||||
latest: '/api/post/latest',
|
||||
search: '/api/post/search',
|
||||
},
|
||||
product: {
|
||||
list: '/api/product/list',
|
||||
details: '/api/product/details',
|
||||
search: '/api/product/search',
|
||||
save: '/api/product/saveProduct',
|
||||
create: '/api/product/create',
|
||||
update: '/api/product/update',
|
||||
delete: '/api/product/delete',
|
||||
},
|
||||
user: {
|
||||
list: '/api/user/list',
|
||||
profile: '/api/user/profile',
|
||||
update: '/api/user/update',
|
||||
settings: '/api/user/settings',
|
||||
details: '/api/user/details',
|
||||
},
|
||||
order: {
|
||||
list: '/api/order/list',
|
||||
profile: '/api/order/profile',
|
||||
update: '/api/order/update',
|
||||
settings: '/api/order/settings',
|
||||
details: '/api/order/details',
|
||||
changeStatus: (orderId: string) => `/api/order/changeStatus?orderId=${orderId}`,
|
||||
},
|
||||
invoice: {
|
||||
list: '/api/invoice/list',
|
||||
profile: '/api/invoice/profile',
|
||||
update: '/api/invoice/update',
|
||||
saveInvoice: (invoiceId: string) => `/api/invoice/saveInvoice?invoiceId=${invoiceId}`,
|
||||
settings: '/api/invoice/settings',
|
||||
details: '/api/invoice/details',
|
||||
changeStatus: (invoiceId: string) => `/api/invoice/changeStatus?invoiceId=${invoiceId}`,
|
||||
search: '/api/invoice/search',
|
||||
},
|
||||
//
|
||||
//
|
||||
//
|
||||
partyEvent: {
|
||||
list: '/api/party-event/list',
|
||||
details: '/api/party-event/details',
|
||||
search: '/api/party-event/search',
|
||||
create: '/api/party-event/create',
|
||||
update: '/api/party-event/update',
|
||||
delete: '/api/party-event/delete',
|
||||
numOfEvent: '/api/party-event/numOfEvent',
|
||||
},
|
||||
partyOrder: {
|
||||
create: '/api/party-order/create',
|
||||
delete: '/api/party-order/delete',
|
||||
list: '/api/party-order/list',
|
||||
profile: '/api/party-order/profile',
|
||||
update: '/api/party-order/update',
|
||||
settings: '/api/party-order/settings',
|
||||
details: '/api/party-order/details',
|
||||
changeStatus: (partyOrderId: string) =>
|
||||
`/api/party-order/changeStatus?partyOrderId=${partyOrderId}`,
|
||||
},
|
||||
partyUser: {
|
||||
list: '/api/party-user/list',
|
||||
details: '/api/party-user/details',
|
||||
search: '/api/party-user/search',
|
||||
create: '/api/party-user/create',
|
||||
update: '/api/party-user/update',
|
||||
delete: '/api/party-user/delete',
|
||||
//
|
||||
detailsByPartyUserId: (partyUserId: string) =>
|
||||
`/api/party-user/details?partyUserId=${partyUserId}`,
|
||||
},
|
||||
partyUserAuth: {
|
||||
me: '/api/party-user-auth/me',
|
||||
signIn: '/api/party-user-auth/sign-in',
|
||||
signUp: '/api/party-user-auth/sign-up',
|
||||
},
|
||||
};
|
@@ -0,0 +1,16 @@
|
||||
import { AmplifyResetPasswordView } from 'src/auth/view/amplify';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Reset password | Amplify - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<AmplifyResetPasswordView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { AmplifySignInView } from 'src/auth/view/amplify';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign in | Amplify - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<AmplifySignInView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { AmplifySignUpView } from 'src/auth/view/amplify';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign up | Amplify - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<AmplifySignUpView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { AmplifyUpdatePasswordView } from 'src/auth/view/amplify';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Update password | Amplify - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<AmplifyUpdatePasswordView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { AmplifyVerifyView } from 'src/auth/view/amplify';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Verify | Amplify - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<AmplifyVerifyView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
import { SplashScreen } from 'src/components/loading-screen';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function CallbackPage() {
|
||||
return <SplashScreen />;
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { Auth0SignInView } from 'src/auth/view/auth0';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign in | Auth0 - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<Auth0SignInView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { FirebaseResetPasswordView } from 'src/auth/view/firebase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Reset password | Firebase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<FirebaseResetPasswordView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { FirebaseSignInView } from 'src/auth/view/firebase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign in | Firebase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<FirebaseSignInView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { FirebaseSignUpView } from 'src/auth/view/firebase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign up | Firebase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<FirebaseSignUpView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { FirebaseVerifyView } from 'src/auth/view/firebase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Verify | Firebase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<FirebaseVerifyView />
|
||||
</>
|
||||
);
|
||||
}
|
16
03_source/frontend/src/pages/party-user-auth/jwt/sign-in.tsx
Normal file
16
03_source/frontend/src/pages/party-user-auth/jwt/sign-in.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { JwtSignInView } from 'src/auth/view/party-user-jwt';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign in | Jwt - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<JwtSignInView />
|
||||
</>
|
||||
);
|
||||
}
|
16
03_source/frontend/src/pages/party-user-auth/jwt/sign-up.tsx
Normal file
16
03_source/frontend/src/pages/party-user-auth/jwt/sign-up.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { JwtSignUpView } from 'src/auth/view/party-user-jwt';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign up | Jwt - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<JwtSignUpView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { SupabaseResetPasswordView } from 'src/auth/view/supabase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Reset password | Supabase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<SupabaseResetPasswordView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { SupabaseSignInView } from 'src/auth/view/supabase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign in | Supabase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<SupabaseSignInView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { SupabaseSignUpView } from 'src/auth/view/supabase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Sign up | Supabase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<SupabaseSignUpView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { SupabaseUpdatePasswordView } from 'src/auth/view/supabase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Update password | Supabase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<SupabaseUpdatePasswordView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { SupabaseVerifyView } from 'src/auth/view/supabase';
|
||||
import { CONFIG } from 'src/global-config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const metadata = { title: `Verify | Supabase - ${CONFIG.appName}` };
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{metadata.title}</title>
|
||||
|
||||
<SupabaseVerifyView />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -10,6 +10,8 @@ const ROOTS = {
|
||||
AUTH: '/auth',
|
||||
AUTH_DEMO: '/auth-demo',
|
||||
DASHBOARD: '/dashboard',
|
||||
//
|
||||
PARTY_USER_AUTH: '/party-user-auth',
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -207,4 +209,32 @@ export const paths = {
|
||||
demo: { edit: `${ROOTS.DASHBOARD}/party-user/${MOCK_ID}/edit` },
|
||||
},
|
||||
},
|
||||
partyUserAuth: {
|
||||
jwt: {
|
||||
signIn: `${ROOTS.PARTY_USER_AUTH}/jwt/sign-in`,
|
||||
signUp: `${ROOTS.PARTY_USER_AUTH}/jwt/sign-up`,
|
||||
},
|
||||
//
|
||||
// amplify: {
|
||||
// signIn: `${ROOTS.PARTY_USER_AUTH}/amplify/sign-in`,
|
||||
// verify: `${ROOTS.PARTY_USER_AUTH}/amplify/verify`,
|
||||
// signUp: `${ROOTS.PARTY_USER_AUTH}/amplify/sign-up`,
|
||||
// updatePassword: `${ROOTS.PARTY_USER_AUTH}/amplify/update-password`,
|
||||
// resetPassword: `${ROOTS.PARTY_USER_AUTH}/amplify/reset-password`,
|
||||
// },
|
||||
// firebase: {
|
||||
// signIn: `${ROOTS.PARTY_USER_AUTH}/firebase/sign-in`,
|
||||
// verify: `${ROOTS.PARTY_USER_AUTH}/firebase/verify`,
|
||||
// signUp: `${ROOTS.PARTY_USER_AUTH}/firebase/sign-up`,
|
||||
// resetPassword: `${ROOTS.PARTY_USER_AUTH}/firebase/reset-password`,
|
||||
// },
|
||||
// auth0: { signIn: `${ROOTS.PARTY_USER_AUTH}/auth0/sign-in` },
|
||||
// supabase: {
|
||||
// signIn: `${ROOTS.PARTY_USER_AUTH}/supabase/sign-in`,
|
||||
// verify: `${ROOTS.PARTY_USER_AUTH}/supabase/verify`,
|
||||
// signUp: `${ROOTS.PARTY_USER_AUTH}/supabase/sign-up`,
|
||||
// updatePassword: `${ROOTS.PARTY_USER_AUTH}/supabase/update-password`,
|
||||
// resetPassword: `${ROOTS.PARTY_USER_AUTH}/supabase/reset-password`,
|
||||
// },
|
||||
},
|
||||
};
|
||||
|
@@ -9,6 +9,7 @@ import { authDemoRoutes } from './auth-demo';
|
||||
import { componentsRoutes } from './components';
|
||||
import { dashboardRoutes } from './dashboard';
|
||||
import { mainRoutes } from './main';
|
||||
import { partyUserAuthRoutes } from './party-user-auth';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -48,6 +49,9 @@ export const routesSection: RouteObject[] = [
|
||||
// Components
|
||||
...componentsRoutes,
|
||||
|
||||
// party-user-auth
|
||||
...partyUserAuthRoutes,
|
||||
|
||||
// No match
|
||||
{ path: '*', element: <Page404 /> },
|
||||
];
|
||||
|
282
03_source/frontend/src/routes/sections/party-user-auth.tsx
Normal file
282
03_source/frontend/src/routes/sections/party-user-auth.tsx
Normal file
@@ -0,0 +1,282 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import type { RouteObject } from 'react-router';
|
||||
import { Outlet } from 'react-router';
|
||||
import { GuestGuard } from 'src/auth/guard';
|
||||
import { SplashScreen } from 'src/components/loading-screen';
|
||||
import { AuthSplitLayout } from 'src/layouts/auth-split';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* Jwt
|
||||
*************************************** */
|
||||
const Jwt = {
|
||||
SignInPage: lazy(() => import('src/pages/party-user-auth/jwt/sign-in')),
|
||||
SignUpPage: lazy(() => import('src/pages/party-user-auth/jwt/sign-up')),
|
||||
};
|
||||
|
||||
const authJwt = {
|
||||
path: 'jwt',
|
||||
children: [
|
||||
{
|
||||
path: 'sign-in',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout
|
||||
slotProps={{
|
||||
section: { title: 'Hi, Welcome back' },
|
||||
}}
|
||||
>
|
||||
<Jwt.SignInPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'sign-up',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout>
|
||||
<Jwt.SignUpPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/** **************************************
|
||||
* Amplify
|
||||
*************************************** */
|
||||
const Amplify = {
|
||||
SignInPage: lazy(() => import('src/pages/auth/amplify/sign-in')),
|
||||
SignUpPage: lazy(() => import('src/pages/auth/amplify/sign-up')),
|
||||
VerifyPage: lazy(() => import('src/pages/auth/amplify/verify')),
|
||||
UpdatePasswordPage: lazy(() => import('src/pages/auth/amplify/update-password')),
|
||||
ResetPasswordPage: lazy(() => import('src/pages/auth/amplify/reset-password')),
|
||||
};
|
||||
|
||||
const authAmplify = {
|
||||
path: 'amplify',
|
||||
children: [
|
||||
{
|
||||
path: 'sign-in',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout
|
||||
slotProps={{
|
||||
section: { title: 'Hi, Welcome back' },
|
||||
}}
|
||||
>
|
||||
<Amplify.SignInPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'sign-up',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout>
|
||||
<Amplify.SignUpPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'verify',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Amplify.VerifyPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Amplify.ResetPasswordPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'update-password',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Amplify.UpdatePasswordPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/** **************************************
|
||||
* Firebase
|
||||
*************************************** */
|
||||
const Firebase = {
|
||||
SignInPage: lazy(() => import('src/pages/auth/firebase/sign-in')),
|
||||
SignUpPage: lazy(() => import('src/pages/auth/firebase/sign-up')),
|
||||
VerifyPage: lazy(() => import('src/pages/auth/firebase/verify')),
|
||||
ResetPasswordPage: lazy(() => import('src/pages/auth/firebase/reset-password')),
|
||||
};
|
||||
|
||||
const authFirebase = {
|
||||
path: 'firebase',
|
||||
children: [
|
||||
{
|
||||
path: 'sign-in',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout
|
||||
slotProps={{
|
||||
section: { title: 'Hi, Welcome back' },
|
||||
}}
|
||||
>
|
||||
<Firebase.SignInPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'sign-up',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout>
|
||||
<Firebase.SignUpPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'verify',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Firebase.VerifyPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Firebase.ResetPasswordPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/** **************************************
|
||||
* Auth0
|
||||
*************************************** */
|
||||
const Auth0 = {
|
||||
SignInPage: lazy(() => import('src/pages/auth/auth0/sign-in')),
|
||||
CallbackPage: lazy(() => import('src/pages/auth/auth0/callback')),
|
||||
};
|
||||
|
||||
const authAuth0 = {
|
||||
path: 'auth0',
|
||||
children: [
|
||||
{
|
||||
path: 'sign-in',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout
|
||||
slotProps={{
|
||||
section: { title: 'Hi, Welcome back' },
|
||||
}}
|
||||
>
|
||||
<Auth0.SignInPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'callback',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<Auth0.CallbackPage />
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/** **************************************
|
||||
* Supabase
|
||||
*************************************** */
|
||||
const Supabase = {
|
||||
SignInPage: lazy(() => import('src/pages/auth/supabase/sign-in')),
|
||||
SignUpPage: lazy(() => import('src/pages/auth/supabase/sign-up')),
|
||||
VerifyPage: lazy(() => import('src/pages/auth/supabase/verify')),
|
||||
UpdatePasswordPage: lazy(() => import('src/pages/auth/supabase/update-password')),
|
||||
ResetPasswordPage: lazy(() => import('src/pages/auth/supabase/reset-password')),
|
||||
};
|
||||
|
||||
const authSupabase = {
|
||||
path: 'supabase',
|
||||
children: [
|
||||
{
|
||||
path: 'sign-in',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout
|
||||
slotProps={{
|
||||
section: { title: 'Hi, Welcome back' },
|
||||
}}
|
||||
>
|
||||
<Supabase.SignInPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'sign-up',
|
||||
element: (
|
||||
<GuestGuard>
|
||||
<AuthSplitLayout>
|
||||
<Supabase.SignUpPage />
|
||||
</AuthSplitLayout>
|
||||
</GuestGuard>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'verify',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Supabase.VerifyPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Supabase.ResetPasswordPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'update-password',
|
||||
element: (
|
||||
<AuthSplitLayout>
|
||||
<Supabase.UpdatePasswordPage />
|
||||
</AuthSplitLayout>
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const partyUserAuthRoutes: RouteObject[] = [
|
||||
{
|
||||
path: 'party-user-auth',
|
||||
element: (
|
||||
<Suspense fallback={<SplashScreen />}>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
),
|
||||
children: [authJwt, authAmplify, authFirebase, authAuth0, authSupabase],
|
||||
},
|
||||
];
|
@@ -83,6 +83,44 @@ export function OverviewAppView() {
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ xs: 12, md: 4 }}>
|
||||
<AppWidgetSummary
|
||||
title="Total party-events"
|
||||
percent={-0.1}
|
||||
total={4}
|
||||
chart={{
|
||||
colors: [theme.palette.error.main],
|
||||
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'],
|
||||
series: [],
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ xs: 12, md: 4 }}>
|
||||
<AppWidgetSummary
|
||||
title="Total party-events"
|
||||
percent={-0.1}
|
||||
total={5}
|
||||
chart={{
|
||||
colors: [theme.palette.error.main],
|
||||
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'],
|
||||
series: [],
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ xs: 12, md: 4 }}>
|
||||
<AppWidgetSummary
|
||||
title="Total party-events"
|
||||
percent={-0.1}
|
||||
total={6}
|
||||
chart={{
|
||||
colors: [theme.palette.error.main],
|
||||
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'],
|
||||
series: [],
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6, lg: 4 }}>
|
||||
<AppCurrentDownload
|
||||
title="Current download"
|
||||
|
@@ -36,7 +36,7 @@ import { EmptyContent } from 'src/components/empty-content';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { toast } from 'src/components/snackbar';
|
||||
import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { endpoints } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import { RouterLink } from 'src/routes/components';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import type { IPartyEventItem, IProductTableFilters } from 'src/types/party-event';
|
||||
|
@@ -35,7 +35,7 @@ import { EmptyContent } from 'src/components/empty-content';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { toast } from 'src/components/snackbar';
|
||||
import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { endpoints } from 'src/lib/axios';
|
||||
import { endpoints } from 'src/lib/endpoints';
|
||||
import { RouterLink } from 'src/routes/components';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import type { IProductItem, IProductTableFilters } from 'src/types/product';
|
||||
|
@@ -32,3 +32,17 @@ A: You only need to update the comment with same format and detail levels of the
|
||||
|
||||
Q: which command should i use for check git stage ?
|
||||
A: `git status . ` to avoid hanging in the terminal
|
||||
|
||||
Q: what is the example of `top comment` ?
|
||||
A: please refer to markdown below:
|
||||
|
||||
```markdown
|
||||
// <file-path, starting from `src`>
|
||||
//
|
||||
// PURPOSE:
|
||||
// - T.B.A. (Clear PURPOSE section with bullet points)
|
||||
//
|
||||
// RULES:
|
||||
// - T.B.A. (Clear RULES section with bullet points)
|
||||
//
|
||||
```
|
||||
|
@@ -23,7 +23,7 @@
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"editor.fontSize": 15
|
||||
"editor.fontSize": 18
|
||||
},
|
||||
"extensions": {
|
||||
"recommendations": [
|
||||
|
@@ -1,3 +1,8 @@
|
||||
please read and follow steps in the markdown files in `98_AI_workspace/software-engineer` folder.
|
||||
|
||||
just reply `OK` when you done thanks.
|
||||
|
||||
---
|
||||
|
||||
please list git staged files (`git status .` )
|
||||
update the comment of the top of the files
|
||||
|
Reference in New Issue
Block a user