mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 20:01:59 +00:00
Implement supabase and update user service
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
||||
import isPropValid from '@emotion/is-prop-valid';
|
||||
import { StyleSheetManager } from 'styled-components';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
// Components
|
||||
import AppLayout from './components/AppLayout';
|
||||
@@ -13,9 +14,17 @@ import NotFound from './pages/NotFound';
|
||||
import * as PATH from './constants/path';
|
||||
import Toast from './components/Toast';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 60 * 1000,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<StyleSheetManager shouldForwardProp={shouldForwardProp}>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
@@ -31,7 +40,7 @@ function App() {
|
||||
</StyleSheetManager>
|
||||
|
||||
<Toast />
|
||||
</>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,18 +8,10 @@ const USER_PAGE = {
|
||||
value: 'name',
|
||||
label: 'Sort by name',
|
||||
},
|
||||
{
|
||||
value: 'identifiedCode',
|
||||
label: 'Sort by identified code',
|
||||
},
|
||||
{
|
||||
value: 'phone',
|
||||
label: 'Sort by phone',
|
||||
},
|
||||
{
|
||||
value: 'roomId',
|
||||
label: 'Sort by room',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@ export const DASHBOARD = '/dashboard';
|
||||
export const USER = '/user';
|
||||
export const ROOM = '/room';
|
||||
export const OTHER_PATH = '*';
|
||||
export const BASE_URL = 'https://hotel-management-api.loiphan.com/';
|
||||
export const BASE_URL = 'http://localhost:3000/';
|
||||
export const USER_PATH = 'users';
|
||||
export const ROOM_PATH = 'rooms';
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Database } from '@type/supabase';
|
||||
const supabaseUrl = 'https://pjqujsjzzdrlrgqbxepe.supabase.co'
|
||||
const supabaseKey = import.meta.env.VITE_SUPABASE_KEY
|
||||
const supabase = createClient<Database>(supabaseUrl, supabaseKey!)
|
||||
|
||||
export default supabase;
|
||||
@@ -1,47 +1,6 @@
|
||||
// Constants
|
||||
import { REQUIRED_FIELD_ERROR } from '../constants/formValidateMessage';
|
||||
|
||||
/**
|
||||
* Create query url for search
|
||||
* @param columnSearch Column want to search
|
||||
* @param keySearch Keyword search
|
||||
* @param sort Sort by
|
||||
* @param order Order by
|
||||
* @returns Return query url
|
||||
*/
|
||||
const searchQuery = (
|
||||
columnSearch: string,
|
||||
keySearch: string,
|
||||
sort: string,
|
||||
order: string
|
||||
) => {
|
||||
const phoneParams = keySearch
|
||||
? `${columnSearch}_like=` + keySearch
|
||||
: '';
|
||||
const sortParams = sort
|
||||
? '_sort=' + sort
|
||||
: '';
|
||||
const orderParams = order
|
||||
? '_order=' + order
|
||||
: '';
|
||||
const finalParam = [phoneParams, sortParams, orderParams];
|
||||
let query = '';
|
||||
let isFirstParam = true;
|
||||
|
||||
finalParam.forEach((param) => {
|
||||
if (param) {
|
||||
if (isFirstParam) {
|
||||
query = query.concat('', param);
|
||||
isFirstParam = false;
|
||||
} else {
|
||||
query = query.concat('&', param);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert value to currency format with value
|
||||
* @param value Value need to be converted
|
||||
@@ -64,4 +23,4 @@ const errorMsg = (errorCode: number, msg: string) => {
|
||||
return `Error code: ${errorCode}. Message: ${msg}`;
|
||||
};
|
||||
|
||||
export { errorMsg, searchQuery, formatCurrency, REQUIRED_FIELD_ERROR };
|
||||
export { errorMsg, formatCurrency, REQUIRED_FIELD_ERROR };
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
// Constants
|
||||
import { BASE_URL } from '@constant/path';
|
||||
import { DEFAULT_ORDER_BY, DEFAULT_SORT_BY } from '@constant/config';
|
||||
|
||||
// Helpers
|
||||
import { searchQuery } from '@helper/helper';
|
||||
|
||||
// Types
|
||||
import { Nullable } from '@type/common';
|
||||
|
||||
/**
|
||||
* The function use to fetch data on server API.
|
||||
* @param path The path of url
|
||||
* @param columnSearch Column need to search
|
||||
* @param keyWord The key word to search
|
||||
* @param tempSortBy Sort by
|
||||
* @param tempOrderBy Order by
|
||||
* @param reload Fetch again
|
||||
* @returns data: A data after fetch, isPending: A boolean indicating whether or not the progress of fetch data is done, errorMsg: A error message from the server.
|
||||
*/
|
||||
const useFetch = (
|
||||
path: string,
|
||||
columnSearch: string = '',
|
||||
keyWord: string = '',
|
||||
tempSortBy?: string,
|
||||
tempOrderBy?: string,
|
||||
reload?: boolean
|
||||
) => {
|
||||
const [data, setData] = useState(null);
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
const [errorFetchMsg, setErrorFetchMsg] = useState<Nullable<string>>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Clear data
|
||||
setData(null);
|
||||
|
||||
const fetchData = async () => {
|
||||
setIsPending(true);
|
||||
|
||||
// Set default value
|
||||
const sortBy = tempSortBy
|
||||
? tempSortBy
|
||||
: DEFAULT_SORT_BY;
|
||||
const orderBy = tempOrderBy
|
||||
? tempOrderBy
|
||||
: DEFAULT_ORDER_BY;
|
||||
|
||||
// Query search
|
||||
const query = searchQuery(columnSearch, keyWord, sortBy, orderBy);
|
||||
|
||||
try {
|
||||
const response = await fetch(BASE_URL + path + '?' + query);
|
||||
const json = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error code: ${response.status} \n Messages: ${response.text}`
|
||||
);
|
||||
}
|
||||
|
||||
setIsPending(false);
|
||||
setData(json);
|
||||
setErrorFetchMsg(null);
|
||||
} catch (error) {
|
||||
setErrorFetchMsg(`Could not fetch data.\n ${error}`);
|
||||
setIsPending(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [path, reload, columnSearch, keyWord, tempOrderBy, tempSortBy]);
|
||||
return { data, isPending, errorFetchMsg };
|
||||
};
|
||||
|
||||
export { useFetch };
|
||||
@@ -14,7 +14,6 @@ import RoomRow from './RoomRow';
|
||||
import { IRoom } from '@type/rooms';
|
||||
|
||||
// Constants
|
||||
import { useFetch } from '@hook/useFetch';
|
||||
import { ORDERBY_OPTIONS, ROOM_PAGE } from '@constant/commons';
|
||||
|
||||
// Styled
|
||||
@@ -39,28 +38,7 @@ const RoomTable = ({ reload, setReload }: IRoomTable) => {
|
||||
const orderByValue = searchParams.get('orderBy')
|
||||
? searchParams.get('orderBy')!
|
||||
: '';
|
||||
|
||||
const { data, isPending, errorFetchMsg } = useFetch(
|
||||
'rooms',
|
||||
'name',
|
||||
nameSearch,
|
||||
sortByValue,
|
||||
orderByValue,
|
||||
reload
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setRooms(data);
|
||||
} else {
|
||||
setRooms([]);
|
||||
}
|
||||
|
||||
if (errorFetchMsg) {
|
||||
console.error(errorFetchMsg);
|
||||
}
|
||||
}, [data, errorFetchMsg]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Direction>
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import toast from 'react-hot-toast';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
// Components
|
||||
import { RiEditBoxFill } from 'react-icons/ri';
|
||||
import { IoExit } from 'react-icons/io5';
|
||||
import Modal from '@component/Modal';
|
||||
import Table from '@component/Table';
|
||||
import Menus from '@component/Menus';
|
||||
import UserForm from './UserForm';
|
||||
|
||||
// Constants
|
||||
import { STATUS_CODE } from '@constant/responseStatus';
|
||||
import { CONFIRM_MESSAGE, DENIED_ACTION } from '@constant/messages';
|
||||
|
||||
// Services
|
||||
import { updateRoomStatus } from '@service/roomServices';
|
||||
import { checkOutUser } from '@service/userServices';
|
||||
|
||||
// Types
|
||||
import { IUser } from '@type/users';
|
||||
@@ -27,42 +18,13 @@ interface IUserRow {
|
||||
}
|
||||
|
||||
const UserRow = ({ user, reload, setReload }: IUserRow) => {
|
||||
const handleCheckOut = async (user: IUser) => {
|
||||
if (user.roomId !== 0) {
|
||||
if (confirm(CONFIRM_MESSAGE)) {
|
||||
const resUpdateStatus = await updateRoomStatus(user.roomId, false);
|
||||
const resCheckoutUser = await checkOutUser(user);
|
||||
|
||||
if (
|
||||
resCheckoutUser?.statusCode === STATUS_CODE.OK &&
|
||||
resUpdateStatus?.statusCode === STATUS_CODE.OK
|
||||
) {
|
||||
toast.success('Check out complete!');
|
||||
|
||||
// Reload table
|
||||
setReload(!reload);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toast.error(DENIED_ACTION);
|
||||
}
|
||||
};
|
||||
|
||||
const { id, name, identifiedCode, phone, roomId } = user;
|
||||
const { id, name, phone } = user;
|
||||
|
||||
return (
|
||||
<Table.Row>
|
||||
<div>{id}</div>
|
||||
<div>{name}</div>
|
||||
<div>{identifiedCode}</div>
|
||||
<div>{phone}</div>
|
||||
<div>
|
||||
{
|
||||
roomId
|
||||
? roomId
|
||||
: 'None'
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<Modal>
|
||||
<Menus.Menu>
|
||||
@@ -77,12 +39,6 @@ const UserRow = ({ user, reload, setReload }: IUserRow) => {
|
||||
</Menus.Button>
|
||||
)}
|
||||
/>
|
||||
<Menus.Button
|
||||
icon={<IoExit />}
|
||||
onClick={() => handleCheckOut(user)}
|
||||
>
|
||||
Check out
|
||||
</Menus.Button>
|
||||
</Menus.List>
|
||||
|
||||
<Modal.Window name="edit" title="Edit user">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
||||
|
||||
// Components
|
||||
import Menus from '@component/Menus';
|
||||
@@ -13,9 +13,6 @@ import UserRow from './UserRow';
|
||||
// Types
|
||||
import { IUser } from '@type/users';
|
||||
|
||||
// Hooks
|
||||
import { useFetch } from '@hook/useFetch';
|
||||
|
||||
// Constants
|
||||
import { ORDERBY_OPTIONS, USER_PAGE } from '@constant/commons';
|
||||
|
||||
@@ -24,44 +21,52 @@ import Direction from '@commonStyle/Direction';
|
||||
import { StyledOperationTable } from './styled';
|
||||
import Spinner from '@commonStyle/Spinner';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getAllUsers } from '@service/userServices';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
interface IUserTable {
|
||||
reload: boolean;
|
||||
setReload: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const UserTable = ({ reload, setReload }: IUserTable) => {
|
||||
const columnName = ['Id', 'Name', 'Identified Code', 'Phone', 'Room Id'];
|
||||
const [users, setUsers] = useState<IUser[]>([]);
|
||||
const columnName = ['Id', 'Name', 'Phone'];
|
||||
|
||||
const [phoneSearch, setPhoneSearch] = useState('');
|
||||
const [searchParams] = useSearchParams();
|
||||
const sortByValue = searchParams.get('sortBy')
|
||||
? searchParams.get('sortBy')!
|
||||
: '';
|
||||
: 'id';
|
||||
const orderByValue = searchParams.get('orderBy')
|
||||
? searchParams.get('orderBy')!
|
||||
: '';
|
||||
: 'asc';
|
||||
|
||||
const { data, isPending, errorFetchMsg } = useFetch(
|
||||
'users',
|
||||
'phone',
|
||||
phoneSearch,
|
||||
sortByValue,
|
||||
orderByValue,
|
||||
reload
|
||||
const {
|
||||
isLoading,
|
||||
data: users,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ['cabins', sortByValue, orderByValue, phoneSearch],
|
||||
queryFn: () => getAllUsers(sortByValue, orderByValue, phoneSearch),
|
||||
});
|
||||
|
||||
if(error) {
|
||||
toast.error(error.message);
|
||||
}
|
||||
|
||||
const renderUserRow = useCallback(
|
||||
(user: IUser) => (
|
||||
<UserRow
|
||||
user={user}
|
||||
key={user.id}
|
||||
reload={reload}
|
||||
setReload={setReload}
|
||||
/>
|
||||
),
|
||||
[reload, setReload]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setUsers(data);
|
||||
} else {
|
||||
setUsers([]);
|
||||
}
|
||||
|
||||
if (errorFetchMsg) {
|
||||
console.error(errorFetchMsg);
|
||||
}
|
||||
}, [data, errorFetchMsg, setUsers]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Direction>
|
||||
@@ -75,27 +80,17 @@ const UserTable = ({ reload, setReload }: IUserTable) => {
|
||||
/>
|
||||
</StyledOperationTable>
|
||||
|
||||
{isPending && <Spinner />}
|
||||
{isLoading && <Spinner />}
|
||||
|
||||
{users.length ? (
|
||||
{users && users.length ? (
|
||||
<Menus>
|
||||
<Table columns="10% 30% 20% 20% 10% 5%">
|
||||
<Table columns="10% 40% 35% 15%">
|
||||
<Table.Header headerColumn={columnName} />
|
||||
<Table.Body<IUser>
|
||||
data={users}
|
||||
render={(user: IUser) => (
|
||||
<UserRow
|
||||
user={user}
|
||||
key={user.id}
|
||||
reload={reload}
|
||||
setReload={setReload}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Table.Body<IUser> data={users} render={renderUserRow} />
|
||||
</Table>
|
||||
</Menus>
|
||||
) : (
|
||||
!isPending && <Message>No data to show here!</Message>
|
||||
!isLoading && <Message>No data to show here!</Message>
|
||||
)}
|
||||
</Direction>
|
||||
</>
|
||||
|
||||
@@ -12,6 +12,10 @@ import { IUser } from '@type/users';
|
||||
// Helpers
|
||||
import { sendRequest } from '@helper/sendRequest';
|
||||
import { errorMsg } from '@helper/helper';
|
||||
import supabase from '@constant/supabaseConfig';
|
||||
|
||||
const USERS_TABLE = 'users';
|
||||
const ERROR_FETCHING = "Users can't be loaded!";
|
||||
|
||||
/**
|
||||
* Create user to the server
|
||||
@@ -19,26 +23,26 @@ import { errorMsg } from '@helper/helper';
|
||||
* @returns The IResponse object if success or null
|
||||
*/
|
||||
const createUser = async (user: IUser): Promise<Nullable<IResponse<IUser>>> => {
|
||||
try {
|
||||
const response = await sendRequest<IUser>(
|
||||
USER_PATH,
|
||||
'POST',
|
||||
JSON.stringify(user)
|
||||
);
|
||||
// try {
|
||||
// const response = await sendRequest<IUser>(
|
||||
// USER_PATH,
|
||||
// 'POST',
|
||||
// JSON.stringify(user)
|
||||
// );
|
||||
|
||||
if (response.statusCode !== STATUS_CODE.CREATE) {
|
||||
throw new Error(errorMsg(response.statusCode, response.msg));
|
||||
}
|
||||
// if (response.statusCode !== STATUS_CODE.CREATE) {
|
||||
// throw new Error(errorMsg(response.statusCode, response.msg));
|
||||
// }
|
||||
|
||||
return response;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
toast.error(error.message);
|
||||
}
|
||||
}
|
||||
// return response;
|
||||
// } catch (error: unknown) {
|
||||
// if (error instanceof Error) {
|
||||
// toast.error(error.message);
|
||||
// }
|
||||
// }
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the user to the server
|
||||
@@ -46,23 +50,23 @@ const createUser = async (user: IUser): Promise<Nullable<IResponse<IUser>>> => {
|
||||
* @returns The IResponse object if update success or null
|
||||
*/
|
||||
const updateUser = async (user: IUser): Promise<Nullable<IResponse<IUser>>> => {
|
||||
try {
|
||||
const response = await sendRequest<IUser>(
|
||||
USER_PATH + '/' + user.id,
|
||||
'PUT',
|
||||
JSON.stringify(user)
|
||||
);
|
||||
// try {
|
||||
// const response = await sendRequest<IUser>(
|
||||
// USER_PATH + '/' + user.id,
|
||||
// 'PUT',
|
||||
// JSON.stringify(user)
|
||||
// );
|
||||
|
||||
if (response.statusCode !== STATUS_CODE.OK) {
|
||||
throw new Error(errorMsg(response.statusCode, response.msg));
|
||||
}
|
||||
// if (response.statusCode !== STATUS_CODE.OK) {
|
||||
// throw new Error(errorMsg(response.statusCode, response.msg));
|
||||
// }
|
||||
|
||||
return response;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
toast.error(error.message);
|
||||
}
|
||||
}
|
||||
// return response;
|
||||
// } catch (error: unknown) {
|
||||
// if (error instanceof Error) {
|
||||
// toast.error(error.message);
|
||||
// }
|
||||
// }
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -72,17 +76,37 @@ const updateUser = async (user: IUser): Promise<Nullable<IResponse<IUser>>> => {
|
||||
* @param user The user need to be checkout
|
||||
* @returns The IResponse object if checkout success or not
|
||||
*/
|
||||
const checkOutUser = async (user: IUser): Promise<Nullable<IResponse<IUser>>> => {
|
||||
const tempUser = user;
|
||||
const checkOutUser = async (
|
||||
user: IUser
|
||||
): Promise<Nullable<IResponse<IUser>>> => {
|
||||
// const tempUser = user;
|
||||
|
||||
if (tempUser) {
|
||||
tempUser.roomId = 0;
|
||||
const resUpdateUser = await updateUser(tempUser);
|
||||
// if (tempUser) {
|
||||
// tempUser.roomId = 0;
|
||||
// const resUpdateUser = await updateUser(tempUser);
|
||||
|
||||
return resUpdateUser;
|
||||
}
|
||||
// return resUpdateUser;
|
||||
// }
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export { updateUser, checkOutUser, createUser };
|
||||
const getAllUsers = async (
|
||||
sortBy: string,
|
||||
orderBy: string,
|
||||
phoneSearch: string
|
||||
): Promise<IUser[]> => {
|
||||
const { data, error } = await supabase
|
||||
.from(USERS_TABLE)
|
||||
.select('*')
|
||||
.order(sortBy, { ascending: orderBy === 'asc' })
|
||||
.like('phone', `%${phoneSearch}%`);
|
||||
|
||||
if (error) {
|
||||
throw new Error(ERROR_FETCHING);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export { updateUser, checkOutUser, createUser, getAllUsers };
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json | undefined }
|
||||
| Json[]
|
||||
|
||||
export interface Database {
|
||||
public: {
|
||||
Tables: {
|
||||
bookings: {
|
||||
Row: {
|
||||
amount: number
|
||||
endDate: string
|
||||
id: number
|
||||
roomId: number
|
||||
startDate: string
|
||||
status: boolean
|
||||
userId: number
|
||||
}
|
||||
Insert: {
|
||||
amount: number
|
||||
endDate: string
|
||||
id?: number
|
||||
roomId: number
|
||||
startDate: string
|
||||
status: boolean
|
||||
userId: number
|
||||
}
|
||||
Update: {
|
||||
amount?: number
|
||||
endDate?: string
|
||||
id?: number
|
||||
roomId?: number
|
||||
startDate?: string
|
||||
status?: boolean
|
||||
userId?: number
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "bookings_roomId_fkey"
|
||||
columns: ["roomId"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "rooms"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "bookings_userId_fkey"
|
||||
columns: ["userId"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
rooms: {
|
||||
Row: {
|
||||
id: number
|
||||
name: string
|
||||
price: number
|
||||
status: boolean
|
||||
}
|
||||
Insert: {
|
||||
id?: number
|
||||
name: string
|
||||
price: number
|
||||
status: boolean
|
||||
}
|
||||
Update: {
|
||||
id?: number
|
||||
name?: string
|
||||
price?: number
|
||||
status?: boolean
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
users: {
|
||||
Row: {
|
||||
id: number
|
||||
name: string
|
||||
phone: string
|
||||
}
|
||||
Insert: {
|
||||
id?: number
|
||||
name: string
|
||||
phone: string
|
||||
}
|
||||
Update: {
|
||||
id?: number
|
||||
name?: string
|
||||
phone?: string
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Enums: {
|
||||
[_ in never]: never
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
interface IUser {
|
||||
id: number;
|
||||
name: string;
|
||||
identifiedCode: string;
|
||||
phone: string;
|
||||
roomId: number;
|
||||
}
|
||||
|
||||
export type { IUser };
|
||||
|
||||
Reference in New Issue
Block a user