.
This commit is contained in:
parent
f39dbe95cc
commit
82fdc6de13
@ -1,5 +1,13 @@
|
|||||||
import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom";
|
import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||||
import { DealerLayout, DealersList, LoginPage, LogoutPage, SettingsPage, StartPage } from "./app";
|
import {
|
||||||
|
DealerLayout,
|
||||||
|
DealersList,
|
||||||
|
LoginPage,
|
||||||
|
LogoutPage,
|
||||||
|
SettingsEditor,
|
||||||
|
SettingsLayout,
|
||||||
|
StartPage,
|
||||||
|
} from "./app";
|
||||||
import { CatalogLayout, CatalogList } from "./app/catalog";
|
import { CatalogLayout, CatalogList } from "./app/catalog";
|
||||||
import { DashboardPage } from "./app/dashboard";
|
import { DashboardPage } from "./app/dashboard";
|
||||||
import { QuotesList } from "./app/quotes/list";
|
import { QuotesList } from "./app/quotes/list";
|
||||||
@ -68,9 +76,17 @@ export const Routes = () => {
|
|||||||
path: "/settings",
|
path: "/settings",
|
||||||
element: (
|
element: (
|
||||||
<ProtectedRoute>
|
<ProtectedRoute>
|
||||||
<SettingsPage />
|
<SettingsLayout>
|
||||||
|
<Outlet />
|
||||||
|
</SettingsLayout>
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
),
|
),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: true,
|
||||||
|
element: <SettingsEditor />,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/logout",
|
path: "/logout",
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import { useCatalogList } from "../hooks";
|
|||||||
export const CatalogDataTable = () => {
|
export const CatalogDataTable = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
||||||
console.log("pagination PADRE => ", pagination);
|
|
||||||
|
|
||||||
const { data, isPending, isError, error } = useCatalogList({
|
const { data, isPending, isError, error } = useCatalogList({
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -27,12 +26,30 @@ export const CatalogDataTable = () => {
|
|||||||
|
|
||||||
const columns = useMemo<ColumnDef<IListArticles_Response_DTO, any>[]>(
|
const columns = useMemo<ColumnDef<IListArticles_Response_DTO, any>[]>(
|
||||||
() => [
|
() => [
|
||||||
|
{
|
||||||
|
id: "id" as const,
|
||||||
|
accessorKey: "id",
|
||||||
|
enableResizing: false,
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "article_id" as const,
|
||||||
|
accessorKey: "id_article",
|
||||||
|
enableResizing: false,
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "catalog_name" as const,
|
||||||
|
accessorKey: "catalog_name",
|
||||||
|
enableResizing: false,
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "description" as const,
|
id: "description" as const,
|
||||||
accessorKey: "description",
|
accessorKey: "description",
|
||||||
header: () => <>{t("catalog.list.columns.description")}</>,
|
header: () => <>{t("catalog.list.columns.description")}</>,
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
size: 300,
|
size: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "points" as const,
|
id: "points" as const,
|
||||||
|
|||||||
@ -16,7 +16,6 @@ export const CatalogLayout = ({ children }: PropsWithChildren) => {
|
|||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
1
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</CatalogProvider>
|
</CatalogProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
72
client/src/app/settings/edit.tsx
Normal file
72
client/src/app/settings/edit.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
Input,
|
||||||
|
} from "@/ui";
|
||||||
|
|
||||||
|
import { Checkbox } from "@radix-ui/react-checkbox";
|
||||||
|
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
export const SettingsEditor = () => {
|
||||||
|
return (
|
||||||
|
<div className='mx-auto grid w-full max-w-6xl items-start gap-6 md:grid-cols-[180px_1fr] lg:grid-cols-[250px_1fr]'>
|
||||||
|
<nav className='grid gap-4 text-sm text-muted-foreground' x-chunk='dashboard-04-chunk-0'>
|
||||||
|
<Link to='#' className='font-semibold text-primary'>
|
||||||
|
General
|
||||||
|
</Link>
|
||||||
|
<Link to='#'>Security</Link>
|
||||||
|
<Link to='#'>Integrations</Link>
|
||||||
|
<Link to='#'>Support</Link>
|
||||||
|
<Link to='#'>Organizations</Link>
|
||||||
|
<Link to='#'>Advanced</Link>
|
||||||
|
</nav>
|
||||||
|
<div className='grid gap-6'>
|
||||||
|
<Card x-chunk='dashboard-04-chunk-1'>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Store Name</CardTitle>
|
||||||
|
<CardDescription>Used to identify your store in the marketplace.</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form>
|
||||||
|
<Input placeholder='Store Name' />
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className='px-6 py-4 border-t'>
|
||||||
|
<Button>Save</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
<Card x-chunk='dashboard-04-chunk-2'>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Plugins Directory</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
The directory within your project, in which your plugins are located.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form className='flex flex-col gap-4'>
|
||||||
|
<Input placeholder='Project Name' defaultValue='/content/plugins' />
|
||||||
|
<div className='flex items-center space-x-2'>
|
||||||
|
<Checkbox id='include' defaultChecked />
|
||||||
|
<label
|
||||||
|
htmlFor='include'
|
||||||
|
className='text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
||||||
|
>
|
||||||
|
Allow administrators to change the directory.
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className='px-6 py-4 border-t'>
|
||||||
|
<Button>Save</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,81 +1,2 @@
|
|||||||
import { Layout, LayoutHeader } from "@/components";
|
export * from "./edit";
|
||||||
import {
|
export * from "./layout";
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
Input,
|
|
||||||
} from "@/ui";
|
|
||||||
|
|
||||||
import { Checkbox } from "@radix-ui/react-checkbox";
|
|
||||||
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
export const SettingsPage = () => {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<LayoutHeader />
|
|
||||||
<main className='flex min-h-[calc(100vh_-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:gap-8 md:p-10'>
|
|
||||||
<div className='grid w-full max-w-6xl gap-2 mx-auto'>
|
|
||||||
<h1 className='text-3xl font-semibold'>Settings</h1>
|
|
||||||
</div>
|
|
||||||
<div className='mx-auto grid w-full max-w-6xl items-start gap-6 md:grid-cols-[180px_1fr] lg:grid-cols-[250px_1fr]'>
|
|
||||||
<nav className='grid gap-4 text-sm text-muted-foreground' x-chunk='dashboard-04-chunk-0'>
|
|
||||||
<Link to='#' className='font-semibold text-primary'>
|
|
||||||
General
|
|
||||||
</Link>
|
|
||||||
<Link to='#'>Security</Link>
|
|
||||||
<Link to='#'>Integrations</Link>
|
|
||||||
<Link to='#'>Support</Link>
|
|
||||||
<Link to='#'>Organizations</Link>
|
|
||||||
<Link to='#'>Advanced</Link>
|
|
||||||
</nav>
|
|
||||||
<div className='grid gap-6'>
|
|
||||||
<Card x-chunk='dashboard-04-chunk-1'>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Store Name</CardTitle>
|
|
||||||
<CardDescription>Used to identify your store in the marketplace.</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<form>
|
|
||||||
<Input placeholder='Store Name' />
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className='px-6 py-4 border-t'>
|
|
||||||
<Button>Save</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
<Card x-chunk='dashboard-04-chunk-2'>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Plugins Directory</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
The directory within your project, in which your plugins are located.
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<form className='flex flex-col gap-4'>
|
|
||||||
<Input placeholder='Project Name' defaultValue='/content/plugins' />
|
|
||||||
<div className='flex items-center space-x-2'>
|
|
||||||
<Checkbox id='include' defaultChecked />
|
|
||||||
<label
|
|
||||||
htmlFor='include'
|
|
||||||
className='text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
|
||||||
>
|
|
||||||
Allow administrators to change the directory.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className='px-6 py-4 border-t'>
|
|
||||||
<Button>Save</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
19
client/src/app/settings/layout.tsx
Normal file
19
client/src/app/settings/layout.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Layout, LayoutContent, LayoutHeader } from "@/components";
|
||||||
|
import { PropsWithChildren } from "react";
|
||||||
|
import { Trans } from "react-i18next";
|
||||||
|
|
||||||
|
export const SettingsLayout = ({ children }: PropsWithChildren) => {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<LayoutHeader />
|
||||||
|
<LayoutContent>
|
||||||
|
<div className='grid w-full max-w-6xl gap-2 mx-auto'>
|
||||||
|
<h1 className='text-2xl font-semibold md:text-3xl'>
|
||||||
|
<Trans i18nKey='settings.title' />
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</LayoutContent>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import { DEFAULT_PAGE_SIZES, INITIAL_PAGE_INDEX } from "@/lib/hooks";
|
import { DEFAULT_PAGE_SIZES } from "@/lib/hooks";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/ui";
|
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/ui";
|
||||||
|
import { INITIAL_PAGE_INDEX } from "@shared/contexts";
|
||||||
import { Table } from "@tanstack/react-table";
|
import { Table } from "@tanstack/react-table";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { UserButton } from "./components/UserButton";
|
|||||||
|
|
||||||
export const LayoutHeader = () => {
|
export const LayoutHeader = () => {
|
||||||
return (
|
return (
|
||||||
<header className='sticky top-0 flex items-center h-16 gap-8 px-4 border-b bg-background md:px-6'>
|
<header className='sticky top-0 z-10 flex items-center h-16 gap-8 px-4 border-b bg-background md:px-6'>
|
||||||
<nav className='flex-col hidden gap-6 text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6'>
|
<nav className='flex-col hidden gap-6 text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6'>
|
||||||
<Link to='/' className='flex items-center font-semibold'>
|
<Link to='/' className='flex items-center font-semibold'>
|
||||||
<UeckoLogo className='w-24' />
|
<UeckoLogo className='w-24' />
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { IListResponse_DTO } from "@shared/contexts";
|
import { IListResponse_DTO, INITIAL_PAGE_INDEX, INITIAL_PAGE_SIZE } from "@shared/contexts";
|
||||||
import {
|
import {
|
||||||
ICreateOneDataProviderParams,
|
ICreateOneDataProviderParams,
|
||||||
IDataSource,
|
IDataSource,
|
||||||
@ -10,7 +10,6 @@ import {
|
|||||||
ISortItemDataProviderParam,
|
ISortItemDataProviderParam,
|
||||||
IUpdateOneDataProviderParams,
|
IUpdateOneDataProviderParams,
|
||||||
} from "../hooks/useDataSource/DataSource";
|
} from "../hooks/useDataSource/DataSource";
|
||||||
import { INITIAL_PAGE_INDEX, INITIAL_PAGE_SIZE } from "../hooks/usePagination";
|
|
||||||
import { createAxiosInstance } from "./axiosInstance";
|
import { createAxiosInstance } from "./axiosInstance";
|
||||||
|
|
||||||
export const createAxiosDataProvider = (
|
export const createAxiosDataProvider = (
|
||||||
|
|||||||
@ -125,7 +125,7 @@ export function useDataTable<TData, TValue>({
|
|||||||
enableHiding = false,
|
enableHiding = false,
|
||||||
enableRowSelection = false,
|
enableRowSelection = false,
|
||||||
}: UseDataTableProps<TData, TValue>) {
|
}: UseDataTableProps<TData, TValue>) {
|
||||||
const { pagination, setPagination, sorting, setSorting } = useDataTableContext();
|
const { pagination, setPagination, sorting } = useDataTableContext();
|
||||||
|
|
||||||
// Table states
|
// Table states
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
@ -142,7 +142,7 @@ export function useDataTable<TData, TValue>({
|
|||||||
if (typeof updater === "function") {
|
if (typeof updater === "function") {
|
||||||
const newSorting = updater(sorting);
|
const newSorting = updater(sorting);
|
||||||
console.log(newSorting);
|
console.log(newSorting);
|
||||||
setSorting(newSorting);
|
//setSorting(newSorting);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import { act, renderHook } from "@testing-library/react-hooks";
|
|
||||||
import {
|
import {
|
||||||
INITIAL_PAGE_INDEX,
|
INITIAL_PAGE_INDEX,
|
||||||
INITIAL_PAGE_SIZE,
|
INITIAL_PAGE_SIZE,
|
||||||
MAX_PAGE_SIZE,
|
MAX_PAGE_SIZE,
|
||||||
MIN_PAGE_SIZE,
|
MIN_PAGE_SIZE,
|
||||||
PaginationState,
|
} from "@shared/contexts";
|
||||||
usePagination,
|
import { act, renderHook } from "@testing-library/react-hooks";
|
||||||
} from "./usePagination";
|
import { PaginationState, usePagination } from "./usePagination";
|
||||||
|
|
||||||
describe("usePagination", () => {
|
describe("usePagination", () => {
|
||||||
it("should initialize with default values", () => {
|
it("should initialize with default values", () => {
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
|
import {
|
||||||
|
INITIAL_PAGE_INDEX,
|
||||||
|
INITIAL_PAGE_SIZE,
|
||||||
|
MAX_PAGE_SIZE,
|
||||||
|
MIN_PAGE_SIZE,
|
||||||
|
} from "@shared/contexts";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export const INITIAL_PAGE_INDEX = 0;
|
export const DEFAULT_PAGE_SIZES = [15, 30, 50, 75, 100];
|
||||||
export const INITIAL_PAGE_SIZE = 10;
|
|
||||||
|
|
||||||
export const MIN_PAGE_INDEX = 0;
|
|
||||||
export const MIN_PAGE_SIZE = 1;
|
|
||||||
|
|
||||||
export const MAX_PAGE_SIZE = 100; //Number.MAX_SAFE_INTEGER;
|
|
||||||
|
|
||||||
export const DEFAULT_PAGE_SIZES = [10, 25, 50, 100];
|
|
||||||
|
|
||||||
export interface PaginationState {
|
export interface PaginationState {
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { PaginationState } from "@tanstack/react-table";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
import { useSearchParams } from "react-router-dom";
|
|
||||||
import {
|
import {
|
||||||
INITIAL_PAGE_INDEX,
|
INITIAL_PAGE_INDEX,
|
||||||
INITIAL_PAGE_SIZE,
|
INITIAL_PAGE_SIZE,
|
||||||
MAX_PAGE_SIZE,
|
MAX_PAGE_SIZE,
|
||||||
MIN_PAGE_SIZE,
|
MIN_PAGE_SIZE,
|
||||||
usePagination,
|
} from "@shared/contexts";
|
||||||
} from "./usePagination";
|
import { PaginationState } from "@tanstack/react-table";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
|
import { usePagination } from "./usePagination";
|
||||||
|
|
||||||
export const usePaginationParams = (
|
export const usePaginationParams = (
|
||||||
initialPageIndex: number = INITIAL_PAGE_INDEX,
|
initialPageIndex: number = INITIAL_PAGE_INDEX,
|
||||||
|
|||||||
@ -62,6 +62,9 @@
|
|||||||
"retail_price": "PVP"
|
"retail_price": "PVP"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Ajustes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,9 @@
|
|||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": [
|
||||||
|
"src",
|
||||||
|
"../shared/lib/contexts/common/domain/entities/QueryCriteria/Pagination/defaults.ts"
|
||||||
|
],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,13 @@ module.exports = {
|
|||||||
language: "es",
|
language: "es",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sample_dealer: {
|
||||||
|
name: "Roberto",
|
||||||
|
email: "rblanco@rodax-software.com",
|
||||||
|
password: "123456",
|
||||||
|
language: "en",
|
||||||
|
},
|
||||||
|
|
||||||
uploads: {
|
uploads: {
|
||||||
imports: process.env.UPLOAD_PATH || "/home/rodax/Documentos/BBDD/server/uploads/imports",
|
imports: process.env.UPLOAD_PATH || "/home/rodax/Documentos/BBDD/server/uploads/imports",
|
||||||
documents: process.env.UPLOAD_PATH || "/home/rodax/Documentos/BBDD/server/uploads/documents",
|
documents: process.env.UPLOAD_PATH || "/home/rodax/Documentos/BBDD/server/uploads/documents",
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
export * from "./controllers";
|
export * from "./controllers";
|
||||||
export * from "./passport";
|
export * from "./passport";
|
||||||
export * from "./routes";
|
|
||||||
|
|||||||
@ -1,21 +1,16 @@
|
|||||||
import { AuthUser } from "@/contexts/auth/domain";
|
import { AuthUser } from "@/contexts/auth/domain";
|
||||||
import { composeMiddleware, generateExpressError } from "@/contexts/common/infrastructure/express";
|
import { composeMiddleware, generateExpressError } from "@/contexts/common/infrastructure/express";
|
||||||
import { UniqueID } from "@shared/contexts";
|
import { ensureIdIsValid } from "@shared/contexts";
|
||||||
import Express from "express";
|
import Express from "express";
|
||||||
import httpStatus from "http-status";
|
import httpStatus from "http-status";
|
||||||
import passport from "passport";
|
import passport from "passport";
|
||||||
|
|
||||||
export const isUser = composeMiddleware([
|
export const checkUser = composeMiddleware([
|
||||||
passport.authenticate("local-jwt", {
|
passport.authenticate("local-jwt", {
|
||||||
session: false,
|
session: false,
|
||||||
}),
|
}),
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
console.log(<AuthUser>req.user);
|
|
||||||
/*const user = <AuthUser>req.user;
|
|
||||||
if (!user.isUser) {
|
|
||||||
return generateExpressError(req, res, httpStatus.UNAUTHORIZED);
|
|
||||||
}*/
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,25 +18,34 @@ export const isUser = composeMiddleware([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const isAdmin = composeMiddleware([
|
export const checkisAdmin = composeMiddleware([
|
||||||
isUser,
|
checkUser,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
||||||
const user = <AuthUser>req.user;
|
const user = <AuthUser>req.user;
|
||||||
if (!user.isAdmin) {
|
if (!user.isAdmin) {
|
||||||
generateExpressError(req, res, httpStatus.UNAUTHORIZED);
|
generateExpressError(req, res, httpStatus.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
next();
|
return next();
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const isAdminOrMe = composeMiddleware([
|
export const checkAdminOrSelf = composeMiddleware([
|
||||||
isUser,
|
checkUser,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
||||||
const user = <AuthUser>req.user;
|
const user = <AuthUser>req.user;
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
|
|
||||||
if (user.isAdmin || user.id.equals(UniqueID.create(userId).object)) {
|
if (user.isAdmin) {
|
||||||
next();
|
return next();
|
||||||
} else generateExpressError(req, res, httpStatus.UNAUTHORIZED);
|
}
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
const paramIdOrError = ensureIdIsValid(userId);
|
||||||
|
if (paramIdOrError.isSuccess && user.id.equals(paramIdOrError.object)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateExpressError(req, res, httpStatus.UNAUTHORIZED);
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -14,8 +14,6 @@ export interface IArticleProps {
|
|||||||
catalog_name: Slug;
|
catalog_name: Slug;
|
||||||
id_article: ArticleIdentifier;
|
id_article: ArticleIdentifier;
|
||||||
reference: Slug;
|
reference: Slug;
|
||||||
//family: Description;
|
|
||||||
//subfamily: Description;
|
|
||||||
description: Description;
|
description: Description;
|
||||||
points: Quantity;
|
points: Quantity;
|
||||||
retail_price: UnitPrice;
|
retail_price: UnitPrice;
|
||||||
@ -26,8 +24,6 @@ export interface IArticle {
|
|||||||
catalog_name: Slug;
|
catalog_name: Slug;
|
||||||
id_article: ArticleIdentifier;
|
id_article: ArticleIdentifier;
|
||||||
reference: Slug;
|
reference: Slug;
|
||||||
//family: Description;
|
|
||||||
//subfamily: Description;
|
|
||||||
description: Description;
|
description: Description;
|
||||||
points: Quantity;
|
points: Quantity;
|
||||||
retail_price: UnitPrice;
|
retail_price: UnitPrice;
|
||||||
@ -67,14 +63,6 @@ export class Article extends AggregateRoot<IArticleProps> implements IArticle {
|
|||||||
return this.props.reference;
|
return this.props.reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*get family(): Description {
|
|
||||||
return this.props.family;
|
|
||||||
}
|
|
||||||
|
|
||||||
get subfamily(): Description {
|
|
||||||
return this.props.subfamily;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
get description(): Description {
|
get description(): Description {
|
||||||
return this.props.description;
|
return this.props.description;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
|
|
||||||
import {
|
import { ListArticlesResult, ListArticlesUseCase } from "@/contexts/catalog/application";
|
||||||
ListArticlesResult,
|
|
||||||
ListArticlesUseCase,
|
|
||||||
} from "@/contexts/catalog/application";
|
|
||||||
import { Article } from "@/contexts/catalog/domain";
|
import { Article } from "@/contexts/catalog/domain";
|
||||||
import { QueryCriteriaService } from "@/contexts/common/application/services";
|
import { QueryCriteriaService } from "@/contexts/common/application/services";
|
||||||
import { IServerError } from "@/contexts/common/domain/errors";
|
import { IServerError } from "@/contexts/common/domain/errors";
|
||||||
@ -29,7 +26,7 @@ export class ListArticlesController extends ExpressController {
|
|||||||
useCase: ListArticlesUseCase;
|
useCase: ListArticlesUseCase;
|
||||||
presenter: IListArticlesPresenter;
|
presenter: IListArticlesPresenter;
|
||||||
},
|
},
|
||||||
context: ICatalogContext,
|
context: ICatalogContext
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -60,10 +57,7 @@ export class ListArticlesController extends ExpressController {
|
|||||||
const queryParams = queryOrError.object;
|
const queryParams = queryOrError.object;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const queryCriteria: IQueryCriteria =
|
const queryCriteria: IQueryCriteria = QueryCriteriaService.parse(queryParams);
|
||||||
QueryCriteriaService.parse(queryParams);
|
|
||||||
|
|
||||||
console.log(queryCriteria);
|
|
||||||
|
|
||||||
const result: ListArticlesResult = await this.useCase.execute({
|
const result: ListArticlesResult = await this.useCase.execute({
|
||||||
queryCriteria,
|
queryCriteria,
|
||||||
@ -79,7 +73,7 @@ export class ListArticlesController extends ExpressController {
|
|||||||
this.presenter.mapArray(customers, this.context, {
|
this.presenter.mapArray(customers, this.context, {
|
||||||
page: queryCriteria.pagination.offset,
|
page: queryCriteria.pagination.offset,
|
||||||
limit: queryCriteria.pagination.limit,
|
limit: queryCriteria.pagination.limit,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
return this.fail(e as IServerError);
|
return this.fail(e as IServerError);
|
||||||
|
|||||||
@ -23,8 +23,6 @@ export const listArticlesPresenter: IListArticlesPresenter = {
|
|||||||
id_article: article.id_article.toString(),
|
id_article: article.id_article.toString(),
|
||||||
reference: article.reference.toString(),
|
reference: article.reference.toString(),
|
||||||
description: article.description.toString(),
|
description: article.description.toString(),
|
||||||
//family: article.family.toString(),
|
|
||||||
//subfamily: article.subfamily.toString(),
|
|
||||||
points: article.points.toNumber(),
|
points: article.points.toNumber(),
|
||||||
retail_price: article.retail_price.toObject(),
|
retail_price: article.retail_price.toObject(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,2 +1 @@
|
|||||||
export * from "./controllers";
|
export * from "./controllers";
|
||||||
export * from "./routes";
|
|
||||||
|
|||||||
@ -20,8 +20,6 @@ class ArticleMapper
|
|||||||
catalog_name: this.mapsValue(source, "catalog_name", Slug.create),
|
catalog_name: this.mapsValue(source, "catalog_name", Slug.create),
|
||||||
id_article: this.mapsValue(source, "id_article", ArticleIdentifier.create),
|
id_article: this.mapsValue(source, "id_article", ArticleIdentifier.create),
|
||||||
reference: this.mapsValue(source, "reference", Slug.create),
|
reference: this.mapsValue(source, "reference", Slug.create),
|
||||||
//family: this.mapsValue(source, "family", Description.create),
|
|
||||||
//subfamily: this.mapsValue(source, "subfamily", Description.create),
|
|
||||||
description: this.mapsValue(source, "description", Description.create),
|
description: this.mapsValue(source, "description", Description.create),
|
||||||
points: this.mapsValue(source, "points", Quantity.create),
|
points: this.mapsValue(source, "points", Quantity.create),
|
||||||
retail_price: this.mapsValue(source, "retail_price", (value: any) =>
|
retail_price: this.mapsValue(source, "retail_price", (value: any) =>
|
||||||
|
|||||||
@ -26,8 +26,6 @@ export class Article_Model extends Model<
|
|||||||
declare catalog_name: string;
|
declare catalog_name: string;
|
||||||
declare id_article: string; // number ??
|
declare id_article: string; // number ??
|
||||||
declare reference: CreationOptional<string>;
|
declare reference: CreationOptional<string>;
|
||||||
//declare family: CreationOptional<string>;
|
|
||||||
//declare subfamily: CreationOptional<string>;
|
|
||||||
declare description: CreationOptional<string>;
|
declare description: CreationOptional<string>;
|
||||||
declare points: CreationOptional<number>;
|
declare points: CreationOptional<number>;
|
||||||
declare retail_price: CreationOptional<number>;
|
declare retail_price: CreationOptional<number>;
|
||||||
@ -50,8 +48,6 @@ export default (sequelize: Sequelize) => {
|
|||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
reference: DataTypes.STRING(),
|
reference: DataTypes.STRING(),
|
||||||
//family: DataTypes.STRING(),
|
|
||||||
//subfamily: DataTypes.STRING(),
|
|
||||||
description: DataTypes.STRING(),
|
description: DataTypes.STRING(),
|
||||||
points: {
|
points: {
|
||||||
type: DataTypes.SMALLINT().UNSIGNED,
|
type: DataTypes.SMALLINT().UNSIGNED,
|
||||||
@ -74,8 +70,6 @@ export default (sequelize: Sequelize) => {
|
|||||||
indexes: [
|
indexes: [
|
||||||
{ name: "catalog_name_idx", fields: ["catalog_name"] },
|
{ name: "catalog_name_idx", fields: ["catalog_name"] },
|
||||||
{ name: "id_article_idx", fields: ["id_article"] },
|
{ name: "id_article_idx", fields: ["id_article"] },
|
||||||
//{ name: "family_idx", fields: ["family"] },
|
|
||||||
//{ name: "family_subfamily_idx", fields: ["family", "subfamily"] },
|
|
||||||
{ name: "updated_at_idx", fields: ["updated_at"] },
|
{ name: "updated_at_idx", fields: ["updated_at"] },
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -88,12 +82,6 @@ export default (sequelize: Sequelize) => {
|
|||||||
reference: {
|
reference: {
|
||||||
[Op.like]: `%${value}%`,
|
[Op.like]: `%${value}%`,
|
||||||
},
|
},
|
||||||
/*family: {
|
|
||||||
[Op.like]: `%${value}%`,
|
|
||||||
},
|
|
||||||
subfamily: {
|
|
||||||
[Op.like]: `%${value}%`,
|
|
||||||
},*/
|
|
||||||
description: {
|
description: {
|
||||||
[Op.like]: `%${value}%`,
|
[Op.like]: `%${value}%`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { IRepositoryManager, RepositoryManager } from "@/contexts/common/domain";
|
||||||
|
import {
|
||||||
|
ISequelizeAdapter,
|
||||||
|
createSequelizeAdapter,
|
||||||
|
} from "@/contexts/common/infrastructure/sequelize";
|
||||||
|
|
||||||
|
export interface IProfileContext {
|
||||||
|
adapter: ISequelizeAdapter;
|
||||||
|
repositoryManager: IRepositoryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProfileContext {
|
||||||
|
private static instance: ProfileContext | null = null;
|
||||||
|
|
||||||
|
public static getInstance(): IProfileContext {
|
||||||
|
if (!ProfileContext.instance) {
|
||||||
|
ProfileContext.instance = new ProfileContext({
|
||||||
|
adapter: createSequelizeAdapter(),
|
||||||
|
repositoryManager: RepositoryManager.getInstance(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProfileContext.instance.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private context: IProfileContext;
|
||||||
|
|
||||||
|
private constructor(context: IProfileContext) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
import { IUseCaseError, UseCaseError } from "@/contexts/common/application/useCases";
|
||||||
|
import { ExpressController } from "@/contexts/common/infrastructure/express";
|
||||||
|
import { User } from "@/contexts/users/domain/entities/User";
|
||||||
|
import { IGetUserResponse_DTO } from "@shared/contexts";
|
||||||
|
|
||||||
|
import { IServerError } from "@/contexts/common/domain/errors";
|
||||||
|
import { IInfrastructureError, InfrastructureError } from "@/contexts/common/infrastructure";
|
||||||
|
import { GetDealerByUserUseCase } from "@/contexts/sales/application";
|
||||||
|
import { Dealer } from "@/contexts/sales/domain";
|
||||||
|
import { IProfileContext } from "../../../Profile.context";
|
||||||
|
import { IGetProfilePresenter } from "./presenter";
|
||||||
|
|
||||||
|
export class GetProfileController extends ExpressController {
|
||||||
|
private useCase: GetDealerByUserUseCase;
|
||||||
|
private presenter: IGetProfilePresenter;
|
||||||
|
private context: IProfileContext;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
props: {
|
||||||
|
useCase: GetDealerByUserUseCase;
|
||||||
|
presenter: IGetProfilePresenter;
|
||||||
|
},
|
||||||
|
context: IProfileContext
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const { useCase, presenter } = props;
|
||||||
|
this.useCase = useCase;
|
||||||
|
this.presenter = presenter;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeImpl(): Promise<any> {
|
||||||
|
const user = <User | undefined>this.req.user;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
const errorMessage = "Unexpected missing user data";
|
||||||
|
const infraError = InfrastructureError.create(
|
||||||
|
InfrastructureError.UNEXCEPTED_ERROR,
|
||||||
|
errorMessage
|
||||||
|
);
|
||||||
|
return this.internalServerError(errorMessage, infraError);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.useCase.execute({
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.isFailure) {
|
||||||
|
return this._handleExecuteError(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dealer = <Dealer>result.object;
|
||||||
|
|
||||||
|
return this.ok<IGetUserResponse_DTO>(this.presenter.map(user, dealer, this.context));
|
||||||
|
} catch (e: unknown) {
|
||||||
|
return this.fail(e as IServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleExecuteError(error: IUseCaseError) {
|
||||||
|
let errorMessage: string;
|
||||||
|
let infraError: IInfrastructureError;
|
||||||
|
|
||||||
|
switch (error.code) {
|
||||||
|
case UseCaseError.NOT_FOUND_ERROR:
|
||||||
|
errorMessage = "User has no associated profile";
|
||||||
|
|
||||||
|
infraError = InfrastructureError.create(
|
||||||
|
InfrastructureError.RESOURCE_NOT_READY,
|
||||||
|
errorMessage,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.notFoundError(errorMessage, infraError);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UseCaseError.UNEXCEPTED_ERROR:
|
||||||
|
errorMessage = error.message;
|
||||||
|
|
||||||
|
infraError = InfrastructureError.create(
|
||||||
|
InfrastructureError.UNEXCEPTED_ERROR,
|
||||||
|
errorMessage,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
return this.internalServerError(errorMessage, infraError);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorMessage = error.message;
|
||||||
|
return this.clientError(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { GetDealerByUserUseCase } from "@/contexts/sales/application";
|
||||||
|
import { registerDealerRepository } from "@/contexts/sales/infrastructure/Dealer.repository";
|
||||||
|
import { IProfileContext } from "../../../Profile.context";
|
||||||
|
import { GetProfileController } from "./GetProfile.controller";
|
||||||
|
import { GetUserPresenter } from "./presenter";
|
||||||
|
|
||||||
|
export const createGetProfileController = (context: IProfileContext) => {
|
||||||
|
registerDealerRepository(context);
|
||||||
|
|
||||||
|
return new GetProfileController(
|
||||||
|
{
|
||||||
|
useCase: new GetDealerByUserUseCase(context),
|
||||||
|
presenter: GetUserPresenter,
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { IProfileContext } from "@/contexts/profile/infrastructure/Profile.context";
|
||||||
|
import { Dealer } from "@/contexts/sales/domain";
|
||||||
|
import { User } from "@/contexts/users/domain";
|
||||||
|
import { IGetProfileResponse_DTO } from "@shared/contexts";
|
||||||
|
|
||||||
|
export interface IGetProfilePresenter {
|
||||||
|
map: (user: User, dealer: Dealer, context: IProfileContext) => IGetProfileResponse_DTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GetUserPresenter: IGetProfilePresenter = {
|
||||||
|
map: (user: User, dealer: Dealer, context: IProfileContext): IGetProfileResponse_DTO => {
|
||||||
|
return {
|
||||||
|
id: user.id.toString(),
|
||||||
|
name: user.name.toString(),
|
||||||
|
email: user.email.toString(),
|
||||||
|
language: "es",
|
||||||
|
roles: user.getRoles().map((rol) => rol.toString()),
|
||||||
|
contact_information: "",
|
||||||
|
default_payment_method: "",
|
||||||
|
default_notes: "",
|
||||||
|
default_legal_terms: "",
|
||||||
|
default_quote_validity: "",
|
||||||
|
status: "active",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./GetUser.presenter";
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./getProfile";
|
||||||
|
//export * from "./updateProfile";
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./controllers";
|
||||||
2
server/src/contexts/profile/infrastructure/index.ts
Normal file
2
server/src/contexts/profile/infrastructure/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./Profile.context";
|
||||||
|
export * from "./express";
|
||||||
@ -2,11 +2,13 @@ import { AggregateRoot, IDomainError, Name, Result, UniqueID } from "@shared/con
|
|||||||
|
|
||||||
export interface IDealerProps {
|
export interface IDealerProps {
|
||||||
name: Name;
|
name: Name;
|
||||||
|
user_id: UniqueID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDealer {
|
export interface IDealer {
|
||||||
id: UniqueID;
|
id: UniqueID;
|
||||||
name: Name;
|
name: Name;
|
||||||
|
user_id: UniqueID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Dealer extends AggregateRoot<IDealerProps> implements IDealer {
|
export class Dealer extends AggregateRoot<IDealerProps> implements IDealer {
|
||||||
@ -18,4 +20,8 @@ export class Dealer extends AggregateRoot<IDealerProps> implements IDealer {
|
|||||||
get name(): Name {
|
get name(): Name {
|
||||||
return this.props.name;
|
return this.props.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get user_id(): UniqueID {
|
||||||
|
return this.props.user_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,20 +53,7 @@ export class DealerRepository extends SequelizeRepository<Dealer> implements IDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getByUserId(userId: UniqueID): Promise<Dealer | null> {
|
public async getByUserId(userId: UniqueID): Promise<Dealer | null> {
|
||||||
const _dealer_model = this._adapter.getModel("Dealer_Model");
|
const rawDealer: any = await this._getBy("Dealer_Model", "user_id", userId.toPrimitive());
|
||||||
const _user_model = this._adapter.getModel("User_Model");
|
|
||||||
|
|
||||||
const rawDealer: any = await _dealer_model.findOne({
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
attributes: [],
|
|
||||||
model: _user_model,
|
|
||||||
as: "users",
|
|
||||||
required: true,
|
|
||||||
where: { id: userId.toPrimitive() },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!rawDealer === true) {
|
if (!rawDealer === true) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from "./dealers";
|
export * from "./dealers";
|
||||||
|
export * from "./quotes";
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
import { isAdmin, isUser } from "@/contexts/auth";
|
|
||||||
import Express from "express";
|
|
||||||
import {
|
|
||||||
createDealerController,
|
|
||||||
deleteDealerController,
|
|
||||||
getDealerController,
|
|
||||||
updateDealerController,
|
|
||||||
} from "./controllers/dealers";
|
|
||||||
import { listDealersController } from "./controllers/dealers/listDealers";
|
|
||||||
import { getDealerMiddleware } from "./middlewares/dealerMiddleware";
|
|
||||||
import { quoteRoutes } from "./quote.routes";
|
|
||||||
|
|
||||||
export const DealerRouter = (appRouter: Express.Router) => {
|
|
||||||
const dealerRoutes: Express.Router = Express.Router({ mergeParams: true });
|
|
||||||
|
|
||||||
dealerRoutes.get("/", isAdmin, listDealersController);
|
|
||||||
dealerRoutes.get("/:dealerId", isUser, getDealerMiddleware, getDealerController);
|
|
||||||
dealerRoutes.post("/", isAdmin, createDealerController);
|
|
||||||
dealerRoutes.put("/:dealerId", isAdmin, updateDealerController);
|
|
||||||
dealerRoutes.delete("/:dealerId", isAdmin, deleteDealerController);
|
|
||||||
|
|
||||||
// Anidar quotes en /dealers/:dealerId
|
|
||||||
dealerRoutes.use("/:dealerId/quotes", quoteRoutes);
|
|
||||||
|
|
||||||
appRouter.use("/dealers", dealerRoutes);
|
|
||||||
};
|
|
||||||
@ -1 +1 @@
|
|||||||
export * from "./routes";
|
export * from "../../../../infrastructure/express/api/routes/sales.routes";
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from "./Sales.context";
|
export * from "./Sales.context";
|
||||||
|
export * from "./express";
|
||||||
|
|||||||
@ -22,6 +22,7 @@ class DealerMapper
|
|||||||
protected toDomainMappingImpl(source: Dealer_Model, params: any): Dealer {
|
protected toDomainMappingImpl(source: Dealer_Model, params: any): Dealer {
|
||||||
const props: IDealerProps = {
|
const props: IDealerProps = {
|
||||||
name: this.mapsValue(source, "name", Name.create),
|
name: this.mapsValue(source, "name", Name.create),
|
||||||
|
user_id: this.mapsValue(source, "user_id", UniqueID.create),
|
||||||
};
|
};
|
||||||
|
|
||||||
const id = this.mapsValue(source, "id", UniqueID.create);
|
const id = this.mapsValue(source, "id", UniqueID.create);
|
||||||
@ -37,7 +38,8 @@ class DealerMapper
|
|||||||
protected toPersistenceMappingImpl(source: Dealer, params?: MapperParamsType | undefined) {
|
protected toPersistenceMappingImpl(source: Dealer, params?: MapperParamsType | undefined) {
|
||||||
return {
|
return {
|
||||||
id: source.id.toPrimitive(),
|
id: source.id.toPrimitive(),
|
||||||
id_contact: undefined,
|
user_id: source.user_id.toPrimitive(),
|
||||||
|
contact_id: undefined,
|
||||||
name: source.name.toPrimitive(),
|
name: source.name.toPrimitive(),
|
||||||
contact_information: "",
|
contact_information: "",
|
||||||
default_payment_method: "",
|
default_payment_method: "",
|
||||||
@ -45,6 +47,7 @@ class DealerMapper
|
|||||||
default_legal_terms: "",
|
default_legal_terms: "",
|
||||||
default_quote_validity: "",
|
default_quote_validity: "",
|
||||||
status: DEALER_STATUS.STATUS_ACTIVE,
|
status: DEALER_STATUS.STATUS_ACTIVE,
|
||||||
|
language: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
|
import { User_Model } from "@/contexts/users";
|
||||||
import {
|
import {
|
||||||
DataTypes,
|
DataTypes,
|
||||||
InferAttributes,
|
InferAttributes,
|
||||||
InferCreationAttributes,
|
InferCreationAttributes,
|
||||||
Model,
|
Model,
|
||||||
|
NonAttribute,
|
||||||
Op,
|
Op,
|
||||||
Sequelize,
|
Sequelize,
|
||||||
} from "sequelize";
|
} from "sequelize";
|
||||||
@ -13,11 +15,14 @@ export enum DEALER_STATUS {
|
|||||||
STATUS_BLOCKED = "blocked",
|
STATUS_BLOCKED = "blocked",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DealerCreationAttributes = InferCreationAttributes<Dealer_Model>;
|
export type DealerCreationAttributes = InferCreationAttributes<
|
||||||
|
Dealer_Model,
|
||||||
|
{ omit: "user" | "quotes" }
|
||||||
|
>;
|
||||||
|
|
||||||
export class Dealer_Model extends Model<
|
export class Dealer_Model extends Model<
|
||||||
InferAttributes<Dealer_Model>,
|
InferAttributes<Dealer_Model, { omit: "user" | "quotes" }>,
|
||||||
InferCreationAttributes<Dealer_Model>
|
InferCreationAttributes<Dealer_Model, { omit: "user" | "quotes" }>
|
||||||
> {
|
> {
|
||||||
// To avoid table creation
|
// To avoid table creation
|
||||||
/*static async sync(): Promise<any> {
|
/*static async sync(): Promise<any> {
|
||||||
@ -28,9 +33,9 @@ export class Dealer_Model extends Model<
|
|||||||
static associate(connection: Sequelize) {
|
static associate(connection: Sequelize) {
|
||||||
const { Dealer_Model, User_Model } = connection.models;
|
const { Dealer_Model, User_Model } = connection.models;
|
||||||
|
|
||||||
Dealer_Model.hasMany(User_Model, {
|
Dealer_Model.belongsTo(User_Model, {
|
||||||
as: "users",
|
as: "user",
|
||||||
foreignKey: "dealer_id",
|
foreignKey: "user_id",
|
||||||
onDelete: "RESTRICT",
|
onDelete: "RESTRICT",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,7 +47,7 @@ export class Dealer_Model extends Model<
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare id: string;
|
declare id: string;
|
||||||
declare id_contact?: string; // number ??
|
declare contact_id?: string; // number ??
|
||||||
declare name: string;
|
declare name: string;
|
||||||
declare contact_information: string;
|
declare contact_information: string;
|
||||||
declare default_payment_method: string;
|
declare default_payment_method: string;
|
||||||
@ -51,6 +56,9 @@ export class Dealer_Model extends Model<
|
|||||||
declare default_quote_validity: string;
|
declare default_quote_validity: string;
|
||||||
declare status: DEALER_STATUS;
|
declare status: DEALER_STATUS;
|
||||||
declare language: string;
|
declare language: string;
|
||||||
|
|
||||||
|
declare user?: NonAttribute<User_Model>;
|
||||||
|
declare quotes?: NonAttribute<Quote_Model>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (sequelize: Sequelize) => {
|
export default (sequelize: Sequelize) => {
|
||||||
@ -61,7 +69,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
id_contact: {
|
contact_id: {
|
||||||
type: DataTypes.BIGINT().UNSIGNED,
|
type: DataTypes.BIGINT().UNSIGNED,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
@ -96,7 +104,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
deletedAt: "deleted_at",
|
deletedAt: "deleted_at",
|
||||||
|
|
||||||
indexes: [
|
indexes: [
|
||||||
{ name: "id_contact_idx", fields: ["id_contact"] },
|
{ name: "contact_id_idx", fields: ["contact_id"] },
|
||||||
{ name: "status_idx", fields: ["status"] },
|
{ name: "status_idx", fields: ["status"] },
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { config } from "@/config";
|
import { config } from "@/config";
|
||||||
import { IAdapter, Password, RepositoryBuilder } from "@/contexts/common/domain";
|
import { IAdapter, Password, RepositoryBuilder } from "@/contexts/common/domain";
|
||||||
|
import { Dealer, IDealerRepository } from "@/contexts/sales/domain";
|
||||||
import { Email, Language, Name, UniqueID } from "@shared/contexts";
|
import { Email, Language, Name, UniqueID } from "@shared/contexts";
|
||||||
import { IUserRepository, User, UserRole } from "../domain";
|
import { IUserRepository, User, UserRole } from "../domain";
|
||||||
|
|
||||||
@ -76,3 +77,51 @@ export const initializeAdmin = async (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const initializeSampleUser = async (
|
||||||
|
adapter: IAdapter,
|
||||||
|
repository: RepositoryBuilder<IUserRepository>
|
||||||
|
) => {
|
||||||
|
return await adapter.startTransaction().complete(async (t) => {
|
||||||
|
const email = Email.create(config.sample_dealer.email).object;
|
||||||
|
const userExists = await repository({ transaction: t }).existsUserWithEmail(email);
|
||||||
|
|
||||||
|
if (!userExists) {
|
||||||
|
const user = User.create(
|
||||||
|
{
|
||||||
|
name: Name.create(config.sample_dealer.name).object,
|
||||||
|
email,
|
||||||
|
password: Password.createFromPlainTextPassword(config.sample_dealer.password).object,
|
||||||
|
language: Language.createFromCode(config.sample_dealer.language).object,
|
||||||
|
roles: [UserRole.ROLE_USER],
|
||||||
|
},
|
||||||
|
UniqueID.generateNewID().object
|
||||||
|
).object;
|
||||||
|
await repository({ transaction: t }).create(user);
|
||||||
|
console.log("Usuario creado");
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initializeSampleDealer = async (
|
||||||
|
user: User,
|
||||||
|
adapter: IAdapter,
|
||||||
|
repository: RepositoryBuilder<IDealerRepository>
|
||||||
|
) => {
|
||||||
|
return await adapter.startTransaction().complete(async (t) => {
|
||||||
|
const dealerExists = await repository({ transaction: t }).getByUserId(user.id);
|
||||||
|
|
||||||
|
if (!dealerExists) {
|
||||||
|
const dealer = Dealer.create(
|
||||||
|
{
|
||||||
|
name: Name.create(config.sample_dealer.name).object,
|
||||||
|
user_id: user.id,
|
||||||
|
},
|
||||||
|
UniqueID.generateNewID().object
|
||||||
|
).object;
|
||||||
|
await repository({ transaction: t }).create(dealer);
|
||||||
|
console.log("Dealer creado");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
|
export * from "../../../../infrastructure/express/api/routes/users.routes";
|
||||||
export * from "./controllers";
|
export * from "./controllers";
|
||||||
export * from "./routes";
|
|
||||||
|
|||||||
@ -27,9 +27,9 @@ export class User_Model extends Model<
|
|||||||
static associate(connection: Sequelize) {
|
static associate(connection: Sequelize) {
|
||||||
const { User_Model, Dealer_Model } = connection.models;
|
const { User_Model, Dealer_Model } = connection.models;
|
||||||
|
|
||||||
User_Model.belongsTo(Dealer_Model, {
|
User_Model.hasOne(Dealer_Model, {
|
||||||
as: "dealer",
|
as: "dealer",
|
||||||
foreignKey: "dealer_id",
|
foreignKey: "user_id",
|
||||||
onDelete: "RESTRICT",
|
onDelete: "RESTRICT",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import Express from "express";
|
import Express from "express";
|
||||||
import passport from "passport";
|
import passport from "passport";
|
||||||
import { createLoginController } from "./controllers";
|
import { createLoginController } from "../../../../contexts/auth/infrastructure/express/controllers";
|
||||||
import { createIdentityController } from "./controllers/identity";
|
import { createIdentityController } from "../../../../contexts/auth/infrastructure/express/controllers/identity";
|
||||||
import { isUser } from "./passport";
|
import { checkUser } from "../../../../contexts/auth/infrastructure/express/passport";
|
||||||
|
|
||||||
export const AuthRouter = (appRouter: Express.Router) => {
|
export const authRouter = (appRouter: Express.Router) => {
|
||||||
const authRoutes: Express.Router = Express.Router({ mergeParams: true });
|
const authRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
|
|
||||||
//appRouter.use(registerMiddleware("isUser", isUser));
|
//appRouter.use(registerMiddleware("isUser", isUser));
|
||||||
@ -17,16 +17,20 @@ export const AuthRouter = (appRouter: Express.Router) => {
|
|||||||
createLoginController(res.locals["context"]).execute(req, res, next)
|
createLoginController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
|
|
||||||
authRoutes.post("/logout", isUser, (req: Express.Request, res: Express.Response) => {
|
authRoutes.post("/logout", checkUser, (req: Express.Request, res: Express.Response) => {
|
||||||
//req.logout(); <-- ??
|
req.logout(function (err) {
|
||||||
return res.status(200).json();
|
if (err) {
|
||||||
|
return res.status(500).json();
|
||||||
|
}
|
||||||
|
return res.status(200).json();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
authRoutes.post("/register");
|
authRoutes.post("/register");
|
||||||
|
|
||||||
authRoutes.get(
|
authRoutes.get(
|
||||||
"/identity",
|
"/identity",
|
||||||
isUser,
|
checkUser,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
createIdentityController(res.locals["context"]).execute(req, res, next)
|
createIdentityController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
@ -1,19 +1,13 @@
|
|||||||
import { isUser } from "@/contexts/auth";
|
import { checkUser } from "@/contexts/auth";
|
||||||
import Express from "express";
|
import Express from "express";
|
||||||
import { listArticlesController } from "./controllers";
|
import { listArticlesController } from "../../../../contexts/catalog/infrastructure/express/controllers";
|
||||||
|
|
||||||
/*catalogRoutes.get(
|
export const catalogRouter = (appRouter: Express.Router) => {
|
||||||
"/:articleId",
|
|
||||||
(req: Request, res: Response, next: NextFunction) =>
|
|
||||||
createGetCustomerController(res.locals["context"]).execute(req, res, next)
|
|
||||||
);*/
|
|
||||||
|
|
||||||
export const CatalogRouter = (appRouter: Express.Router) => {
|
|
||||||
const catalogRoutes: Express.Router = Express.Router({ mergeParams: true });
|
const catalogRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
|
|
||||||
catalogRoutes.get(
|
catalogRoutes.get(
|
||||||
"/",
|
"/",
|
||||||
isUser,
|
checkUser,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
listArticlesController(res.locals["context"]).execute(req, res, next)
|
listArticlesController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { checkUser, checkisAdmin } from "@/contexts/auth";
|
||||||
|
import {
|
||||||
|
createDealerController,
|
||||||
|
deleteDealerController,
|
||||||
|
getDealerController,
|
||||||
|
listDealersController,
|
||||||
|
updateDealerController,
|
||||||
|
} from "@/contexts/sales/infrastructure/express/controllers/dealers";
|
||||||
|
import { getDealerMiddleware } from "@/contexts/sales/infrastructure/express/middlewares/dealerMiddleware";
|
||||||
|
import Express from "express";
|
||||||
|
import { quoteRoutes } from "./quote.routes";
|
||||||
|
5;
|
||||||
|
|
||||||
|
export const DealerRouter = (appRouter: Express.Router) => {
|
||||||
|
const dealerRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
dealerRoutes.get("/", checkisAdmin, listDealersController);
|
||||||
|
dealerRoutes.get("/:dealerId", checkUser, getDealerMiddleware, getDealerController);
|
||||||
|
dealerRoutes.post("/", checkisAdmin, createDealerController);
|
||||||
|
dealerRoutes.put("/:dealerId", checkisAdmin, updateDealerController);
|
||||||
|
dealerRoutes.delete("/:dealerId", checkisAdmin, deleteDealerController);
|
||||||
|
|
||||||
|
// Anidar quotes en /dealers/:dealerId
|
||||||
|
dealerRoutes.use("/:dealerId/quotes", quoteRoutes);
|
||||||
|
|
||||||
|
appRouter.use("/dealers", dealerRoutes);
|
||||||
|
};
|
||||||
7
server/src/infrastructure/express/api/routes/index.ts
Normal file
7
server/src/infrastructure/express/api/routes/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export * from "./auth.routes";
|
||||||
|
export * from "./catalog.routes";
|
||||||
|
export * from "./dealers.routes";
|
||||||
|
export * from "./profile.routes";
|
||||||
|
export * from "./quote.routes";
|
||||||
|
export * from "./sales.routes";
|
||||||
|
export * from "./users.routes";
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { checkUser } from "@/contexts/auth";
|
||||||
|
import { createGetProfileController } from "@/contexts/profile/infrastructure";
|
||||||
|
import { createUpdateUserController } from "@/contexts/users";
|
||||||
|
import Express from "express";
|
||||||
|
|
||||||
|
export const profileRouter = (appRouter: Express.Router) => {
|
||||||
|
const profileRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
profileRoutes.get(
|
||||||
|
"/",
|
||||||
|
checkUser,
|
||||||
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
|
createGetProfileController(res.locals["context"]).execute(req, res, next)
|
||||||
|
);
|
||||||
|
|
||||||
|
profileRoutes.put(
|
||||||
|
"/",
|
||||||
|
checkUser,
|
||||||
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
|
createUpdateUserController(res.locals["context"]).execute(req, res, next)
|
||||||
|
);
|
||||||
|
|
||||||
|
appRouter.use("/profile", profileRoutes);
|
||||||
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { isAdmin } from "@/contexts/auth";
|
import { checkisAdmin } from "@/contexts/auth";
|
||||||
import Express from "express";
|
import Express from "express";
|
||||||
|
|
||||||
export const quoteRoutes: Express.Router = Express.Router({ mergeParams: true });
|
export const quoteRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
@ -9,7 +9,7 @@ quoteRoutes.post("/", isAdmin, createQuoteController);
|
|||||||
quoteRoutes.put("/:quoteId", isAdmin, updateQuoteController);
|
quoteRoutes.put("/:quoteId", isAdmin, updateQuoteController);
|
||||||
quoteRoutes.delete("/:quoteId", isAdmin, deleteQuoteController);*/
|
quoteRoutes.delete("/:quoteId", isAdmin, deleteQuoteController);*/
|
||||||
|
|
||||||
quoteRoutes.get("/", isAdmin, (req, res) => {
|
quoteRoutes.get("/", checkisAdmin, (req, res) => {
|
||||||
console.log(req.params);
|
console.log(req.params);
|
||||||
res.status(200).json();
|
res.status(200).json();
|
||||||
});
|
});
|
||||||
@ -2,7 +2,7 @@ import Express from "express";
|
|||||||
import { DealerRouter } from "./dealers.routes";
|
import { DealerRouter } from "./dealers.routes";
|
||||||
import { QuoteRouter } from "./quote.routes";
|
import { QuoteRouter } from "./quote.routes";
|
||||||
|
|
||||||
export const SalesRouter = (appRouter: Express.Router) => {
|
export const salesRouter = (appRouter: Express.Router) => {
|
||||||
DealerRouter(appRouter);
|
DealerRouter(appRouter);
|
||||||
QuoteRouter(appRouter);
|
QuoteRouter(appRouter);
|
||||||
};
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { isAdmin, isAdminOrMe } from "@/contexts/auth";
|
import { checkAdminOrSelf, checkisAdmin } from "@/contexts/auth";
|
||||||
import Express from "express";
|
import Express from "express";
|
||||||
import {
|
import {
|
||||||
createCreateUserController,
|
createCreateUserController,
|
||||||
@ -6,42 +6,42 @@ import {
|
|||||||
createGetUserController,
|
createGetUserController,
|
||||||
createListUsersController,
|
createListUsersController,
|
||||||
createUpdateUserController,
|
createUpdateUserController,
|
||||||
} from "./controllers";
|
} from "../../../../contexts/users/infrastructure/express/controllers";
|
||||||
|
|
||||||
export const UserRouter = (appRouter: Express.Router) => {
|
export const usersRouter = (appRouter: Express.Router) => {
|
||||||
const userRoutes: Express.Router = Express.Router({ mergeParams: true });
|
const userRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
|
|
||||||
userRoutes.get(
|
userRoutes.get(
|
||||||
"/",
|
"/",
|
||||||
isAdmin,
|
checkisAdmin,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
createListUsersController(res.locals["context"]).execute(req, res, next)
|
createListUsersController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
|
|
||||||
userRoutes.get(
|
userRoutes.get(
|
||||||
"/:userId",
|
"/:userId",
|
||||||
isAdminOrMe,
|
checkAdminOrSelf,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
createGetUserController(res.locals["context"]).execute(req, res, next)
|
createGetUserController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
|
|
||||||
userRoutes.post(
|
userRoutes.post(
|
||||||
"/",
|
"/",
|
||||||
isAdmin,
|
checkisAdmin,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
createCreateUserController(res.locals["context"]).execute(req, res, next)
|
createCreateUserController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
|
|
||||||
userRoutes.put(
|
userRoutes.put(
|
||||||
"/:userId",
|
"/:userId",
|
||||||
isAdmin,
|
checkisAdmin,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
createUpdateUserController(res.locals["context"]).execute(req, res, next)
|
createUpdateUserController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
|
|
||||||
userRoutes.delete(
|
userRoutes.delete(
|
||||||
"/:userId",
|
"/:userId",
|
||||||
isAdmin,
|
checkisAdmin,
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
createDeleteUserController(res.locals["context"]).execute(req, res, next)
|
createDeleteUserController(res.locals["context"]).execute(req, res, next)
|
||||||
);
|
);
|
||||||
@ -1,9 +1,7 @@
|
|||||||
import { AuthRouter } from "@/contexts/auth";
|
import { salesRouter } from "@/contexts/sales/infrastructure/express";
|
||||||
import { CatalogRouter } from "@/contexts/catalog";
|
|
||||||
import { SalesRouter } from "@/contexts/sales/infrastructure/express";
|
|
||||||
import { UserRouter } from "@/contexts/users";
|
|
||||||
import Express from "express";
|
import Express from "express";
|
||||||
import { createContextMiddleware } from "./context.middleware";
|
import { createContextMiddleware } from "./context.middleware";
|
||||||
|
import { authRouter, catalogRouter, profileRouter, usersRouter } from "./routes";
|
||||||
|
|
||||||
export const v1Routes = () => {
|
export const v1Routes = () => {
|
||||||
const routes = Express.Router({ mergeParams: true });
|
const routes = Express.Router({ mergeParams: true });
|
||||||
@ -23,10 +21,83 @@ export const v1Routes = () => {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
AuthRouter(routes);
|
authRouter(routes);
|
||||||
UserRouter(routes);
|
profileRouter(routes);
|
||||||
CatalogRouter(routes);
|
usersRouter(routes);
|
||||||
SalesRouter(routes);
|
catalogRouter(routes);
|
||||||
|
salesRouter(routes);
|
||||||
|
|
||||||
return routes;
|
return routes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
Comentarios: Cada usuario que no sea administrador tiene un registro asociado en "dealers" a modo de perfil
|
||||||
|
|
||||||
|
* Endpoints Públicos
|
||||||
|
/auth
|
||||||
|
|
||||||
|
POST /auth/login: Permite a los usuarios autenticarse y recibir un token JWT.
|
||||||
|
|
||||||
|
* Endpoints para Usuarios Registrados
|
||||||
|
/auth
|
||||||
|
|
||||||
|
POST /auth/logout: Permite a los usuarios cerrar sesión (podría implicar invalidar el token JWT).
|
||||||
|
GET /auth/identity: Devuelve la identidad del usuario autenticado, incluyendo id, name, email, language y roles.
|
||||||
|
|
||||||
|
/profile
|
||||||
|
|
||||||
|
GET /profile: Obtiene el perfil del usuario autenticado.
|
||||||
|
POST /profile: Actualiza el perfil del usuario autenticado.
|
||||||
|
|
||||||
|
/catalog
|
||||||
|
|
||||||
|
GET /catalog: Devuelve la lista de artículos del catálogo.
|
||||||
|
|
||||||
|
/quotes
|
||||||
|
|
||||||
|
GET /quotes: Devuelve las cotizaciones del usuario autenticado.
|
||||||
|
POST /quotes: Permite al usuario crear una nueva cotización.
|
||||||
|
PUT /quotes/
|
||||||
|
: Permite al usuario actualizar una cotización existente.
|
||||||
|
DELETE /quotes/
|
||||||
|
: Permite al usuario eliminar una cotización existente.
|
||||||
|
|
||||||
|
* Endpoints para Administradores
|
||||||
|
/auth
|
||||||
|
|
||||||
|
POST /auth/register: Permite al administrador registrar nuevos usuarios.
|
||||||
|
|
||||||
|
/users
|
||||||
|
|
||||||
|
GET /users: Devuelve la lista de todos los usuarios.
|
||||||
|
POST /users: Permite crear un nuevo usuario.
|
||||||
|
GET /users/
|
||||||
|
: Devuelve los detalles de un usuario específico.
|
||||||
|
PUT /users/
|
||||||
|
: Permite actualizar los detalles de un usuario específico.
|
||||||
|
DELETE /users/
|
||||||
|
: Permite eliminar un usuario específico.
|
||||||
|
|
||||||
|
/dealers
|
||||||
|
|
||||||
|
GET /dealers: Devuelve la lista de todos los distribuidores.
|
||||||
|
POST /dealers: Permite crear un nuevo distribuidor.
|
||||||
|
GET /dealers/
|
||||||
|
: Devuelve los detalles de un distribuidor específico.
|
||||||
|
PUT /dealers/
|
||||||
|
: Permite actualizar los detalles de un distribuidor específico.
|
||||||
|
DELETE /dealers/
|
||||||
|
: Permite eliminar un distribuidor específico.
|
||||||
|
|
||||||
|
/quotes
|
||||||
|
|
||||||
|
GET /quotes: Devuelve la lista de todas las cotizaciones.
|
||||||
|
POST /quotes: Permite crear una nueva cotización.
|
||||||
|
PUT /quotes/
|
||||||
|
: Permite actualizar una cotización existente.
|
||||||
|
DELETE /quotes/
|
||||||
|
: Permite eliminar una cotización existente.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { trace } from "console";
|
|||||||
import { config } from "../../config";
|
import { config } from "../../config";
|
||||||
import { app } from "../express/app";
|
import { app } from "../express/app";
|
||||||
import { initLogger } from "../logger";
|
import { initLogger } from "../logger";
|
||||||
import { initializeAdminUser } from "../sequelize/initializeAdminUser";
|
import { insertUsers } from "../sequelize/initData";
|
||||||
|
|
||||||
process.env.TZ = "UTC";
|
process.env.TZ = "UTC";
|
||||||
Settings.defaultLocale = "es-ES";
|
Settings.defaultLocale = "es-ES";
|
||||||
@ -109,7 +109,7 @@ try {
|
|||||||
sequelizeConn.sync({ force: false, alter: true }).then(() => {
|
sequelizeConn.sync({ force: false, alter: true }).then(() => {
|
||||||
//
|
//
|
||||||
|
|
||||||
initializeAdminUser();
|
insertUsers();
|
||||||
|
|
||||||
// Launch server
|
// Launch server
|
||||||
server.listen(currentState.server.port, () => {
|
server.listen(currentState.server.port, () => {
|
||||||
|
|||||||
28
server/src/infrastructure/sequelize/initData.ts
Normal file
28
server/src/infrastructure/sequelize/initData.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { registerDealerRepository } from "@/contexts/sales/infrastructure/Dealer.repository";
|
||||||
|
import {
|
||||||
|
initializeAdmin,
|
||||||
|
initializeSampleDealer,
|
||||||
|
initializeSampleUser,
|
||||||
|
} from "@/contexts/users/application/userServices";
|
||||||
|
import { UserContext } from "@/contexts/users/infrastructure/User.context";
|
||||||
|
import { registerUserRepository } from "@/contexts/users/infrastructure/User.repository";
|
||||||
|
|
||||||
|
export const insertUsers = async () => {
|
||||||
|
const context = UserContext.getInstance();
|
||||||
|
registerUserRepository(context);
|
||||||
|
registerDealerRepository(context);
|
||||||
|
|
||||||
|
initializeAdmin(context.adapter, context.repositoryManager.getRepository("User"));
|
||||||
|
const user = await initializeSampleUser(
|
||||||
|
context.adapter,
|
||||||
|
context.repositoryManager.getRepository("User")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
initializeSampleDealer(
|
||||||
|
user,
|
||||||
|
context.adapter,
|
||||||
|
context.repositoryManager.getRepository("Dealer")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { initializeAdmin } from "@/contexts/users/application/userServices";
|
|
||||||
import { UserContext } from "@/contexts/users/infrastructure/User.context";
|
|
||||||
import { registerUserRepository } from "@/contexts/users/infrastructure/User.repository";
|
|
||||||
|
|
||||||
export const initializeAdminUser = () => {
|
|
||||||
const context = UserContext.getInstance();
|
|
||||||
registerUserRepository(context);
|
|
||||||
|
|
||||||
initializeAdmin(context.adapter, context.repositoryManager.getRepository("User"));
|
|
||||||
};
|
|
||||||
@ -2,6 +2,13 @@ import Joi from "joi";
|
|||||||
import { RuleValidator } from "../../../RuleValidator";
|
import { RuleValidator } from "../../../RuleValidator";
|
||||||
import { Result } from "../../Result";
|
import { Result } from "../../Result";
|
||||||
import { ValueObject } from "../../ValueObject";
|
import { ValueObject } from "../../ValueObject";
|
||||||
|
import {
|
||||||
|
INITIAL_PAGE_INDEX,
|
||||||
|
INITIAL_PAGE_SIZE,
|
||||||
|
MAX_PAGE_SIZE,
|
||||||
|
MIN_PAGE_INDEX,
|
||||||
|
MIN_PAGE_SIZE,
|
||||||
|
} from "./defaults";
|
||||||
|
|
||||||
export interface IOffsetPagingProps {
|
export interface IOffsetPagingProps {
|
||||||
offset: number | string | undefined;
|
offset: number | string | undefined;
|
||||||
@ -14,12 +21,12 @@ export interface IOffsetPaging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class OffsetPaging extends ValueObject<IOffsetPaging> {
|
export class OffsetPaging extends ValueObject<IOffsetPaging> {
|
||||||
public static readonly LIMIT_DEFAULT_VALUE: number = 10;
|
public static readonly LIMIT_DEFAULT_VALUE: number = INITIAL_PAGE_SIZE;
|
||||||
public static readonly LIMIT_MINIMAL_VALUE: number = 1;
|
public static readonly LIMIT_MINIMAL_VALUE: number = MIN_PAGE_SIZE;
|
||||||
public static readonly LIMIT_MAXIMAL_VALUE: number = 100;
|
public static readonly LIMIT_MAXIMAL_VALUE: number = MAX_PAGE_SIZE;
|
||||||
|
|
||||||
public static readonly OFFSET_DEFAULT_VALUE: number = 0;
|
public static readonly OFFSET_DEFAULT_VALUE: number = INITIAL_PAGE_INDEX;
|
||||||
public static readonly OFFSET_MINIMAL_VALUE: number = 0;
|
public static readonly OFFSET_MINIMAL_VALUE: number = MIN_PAGE_INDEX;
|
||||||
public static readonly OFFSET_MAXIMAL_VALUE: number = Number.MAX_SAFE_INTEGER;
|
public static readonly OFFSET_MAXIMAL_VALUE: number = Number.MAX_SAFE_INTEGER;
|
||||||
|
|
||||||
public static createWithMaxLimit(): Result<OffsetPaging> {
|
public static createWithMaxLimit(): Result<OffsetPaging> {
|
||||||
@ -52,10 +59,7 @@ export class OffsetPaging extends ValueObject<IOffsetPaging> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static validate(offset: string | number, limit: string | number) {
|
private static validate(offset: string | number, limit: string | number) {
|
||||||
const numberOrError = RuleValidator.validate(
|
const numberOrError = RuleValidator.validate(RuleValidator.RULE_IS_TYPE_NUMBER, offset);
|
||||||
RuleValidator.RULE_IS_TYPE_NUMBER,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (numberOrError.isFailure) {
|
if (numberOrError.isFailure) {
|
||||||
return numberOrError;
|
return numberOrError;
|
||||||
@ -64,25 +68,18 @@ export class OffsetPaging extends ValueObject<IOffsetPaging> {
|
|||||||
const _offset = typeof offset === "string" ? parseInt(offset, 10) : offset;
|
const _offset = typeof offset === "string" ? parseInt(offset, 10) : offset;
|
||||||
|
|
||||||
const offsetValidate = RuleValidator.validate(
|
const offsetValidate = RuleValidator.validate(
|
||||||
Joi.number()
|
Joi.number().min(OffsetPaging.OFFSET_MINIMAL_VALUE).max(OffsetPaging.OFFSET_MAXIMAL_VALUE),
|
||||||
.min(OffsetPaging.OFFSET_MINIMAL_VALUE)
|
offset
|
||||||
.max(OffsetPaging.OFFSET_MAXIMAL_VALUE),
|
|
||||||
offset,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (offsetValidate.isFailure) {
|
if (offsetValidate.isFailure) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new Error(
|
new Error(`Page need to be larger than or equal to ${OffsetPaging.OFFSET_MINIMAL_VALUE}.`)
|
||||||
`Page need to be larger than or equal to ${OffsetPaging.OFFSET_MINIMAL_VALUE}.`,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// limit
|
// limit
|
||||||
const limitNumberOrError = RuleValidator.validate(
|
const limitNumberOrError = RuleValidator.validate(RuleValidator.RULE_IS_TYPE_NUMBER, limit);
|
||||||
RuleValidator.RULE_IS_TYPE_NUMBER,
|
|
||||||
limit,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (limitNumberOrError.isFailure) {
|
if (limitNumberOrError.isFailure) {
|
||||||
return limitNumberOrError;
|
return limitNumberOrError;
|
||||||
@ -92,14 +89,12 @@ export class OffsetPaging extends ValueObject<IOffsetPaging> {
|
|||||||
|
|
||||||
const limitValidate = RuleValidator.validate(
|
const limitValidate = RuleValidator.validate(
|
||||||
Joi.number().min(0).max(OffsetPaging.LIMIT_MAXIMAL_VALUE),
|
Joi.number().min(0).max(OffsetPaging.LIMIT_MAXIMAL_VALUE),
|
||||||
offset,
|
offset
|
||||||
);
|
);
|
||||||
|
|
||||||
if (limitValidate.isFailure) {
|
if (limitValidate.isFailure) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new Error(
|
new Error(`Page size need to be smaller than ${OffsetPaging.LIMIT_MAXIMAL_VALUE}`)
|
||||||
`Page size need to be smaller than ${OffsetPaging.LIMIT_MAXIMAL_VALUE}`,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
export const INITIAL_PAGE_INDEX = 0;
|
||||||
|
export const INITIAL_PAGE_SIZE = 15;
|
||||||
|
|
||||||
|
export const MIN_PAGE_INDEX = 0;
|
||||||
|
export const MIN_PAGE_SIZE = 1;
|
||||||
|
|
||||||
|
export const MAX_PAGE_SIZE = 100; //Number.MAX_SAFE_INTEGER;
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from './OffsetPaging';
|
export * from "./OffsetPaging";
|
||||||
|
export * from "./defaults";
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export * from "./common";
|
|
||||||
|
|
||||||
export * from "./auth";
|
export * from "./auth";
|
||||||
export * from "./catalog";
|
export * from "./catalog";
|
||||||
|
export * from "./common";
|
||||||
|
export * from "./profile";
|
||||||
export * from "./sales";
|
export * from "./sales";
|
||||||
export * from "./users";
|
export * from "./users";
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
export interface IGetProfileResponse_DTO {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
language: string;
|
||||||
|
roles: string[];
|
||||||
|
contact_information: string;
|
||||||
|
default_payment_method: string;
|
||||||
|
default_notes: string;
|
||||||
|
default_legal_terms: string;
|
||||||
|
default_quote_validity: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./IGetProfile_Response.dto";
|
||||||
1
shared/lib/contexts/profile/application/dto/index.ts
Normal file
1
shared/lib/contexts/profile/application/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./GetProfile.dto";
|
||||||
1
shared/lib/contexts/profile/application/index.ts
Normal file
1
shared/lib/contexts/profile/application/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./dto";
|
||||||
1
shared/lib/contexts/profile/index.ts
Normal file
1
shared/lib/contexts/profile/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./application";
|
||||||
Loading…
Reference in New Issue
Block a user