Merge branch 'dev' of https://github.com/smart-chain-fr/leCoffre into dev
5
next-env.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
/// <reference types="next" />
|
|
||||||
/// <reference types="next/image-types/global" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
2982
package-lock.json
generated
30
package.json
@ -4,15 +4,10 @@
|
|||||||
"description": "tezosLink project",
|
"description": "tezosLink project",
|
||||||
"_moduleAliases": {
|
"_moduleAliases": {
|
||||||
"@Api": "./dist/api",
|
"@Api": "./dist/api",
|
||||||
"@Front": "./dist/front",
|
|
||||||
"@Assets": "./dist/front/Assets/*",
|
|
||||||
"@Components": "./dist/front/Components/*",
|
|
||||||
"@Themes": "./dist/front/Themes/*",
|
|
||||||
"@Stores": "./dist/front/Stores/*",
|
|
||||||
"@FrontServices": "./dist/front/Services/*",
|
|
||||||
"@Pages": "./dist/pages",
|
"@Pages": "./dist/pages",
|
||||||
"@Common": "./dist/common",
|
"@Common": "./dist/common",
|
||||||
"@Services": "./dist/common/services",
|
"@Services": "./dist/common/services",
|
||||||
|
"@Repositories": "./dist/common/repositories",
|
||||||
"@Entries": "./dist/common/entries",
|
"@Entries": "./dist/common/entries",
|
||||||
"@Config": "./dist/common/config",
|
"@Config": "./dist/common/config",
|
||||||
"@Entities": "./dist/common/entities",
|
"@Entities": "./dist/common/entities",
|
||||||
@ -20,32 +15,26 @@
|
|||||||
"@ControllerPattern": "./dist/common/system/controller-pattern"
|
"@ControllerPattern": "./dist/common/system/controller-pattern"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "next build && tsc",
|
"build": "tsc",
|
||||||
"api:start": "node ./dist/entries/Api.js",
|
"api:start": "node ./dist/entries/Api.js",
|
||||||
"web:start": "node ./dist/entries/Web.js",
|
|
||||||
"dev": "nodemon -V",
|
"dev": "nodemon -V",
|
||||||
"api:dev": "nodemon -V --exec 'tsc && npm run api:start'",
|
"api:dev": "nodemon -V --exec 'tsc && npm run api:start'",
|
||||||
"web:dev": "nodemon -V --exec 'npm run build && npm run web:start'",
|
|
||||||
"build:test": "tsc && mocha ./dist/entries/Test.js",
|
"build:test": "tsc && mocha ./dist/entries/Test.js",
|
||||||
"format": "prettier --write src",
|
"format": "prettier --write src",
|
||||||
"migrate": ""
|
"migrate": ""
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/smart-chain-fr/tezosLink.git"
|
"url": "git+https://github.com/smart-chain-fr/leCoffre-back.git"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/smart-chain-fr/tezosLink/issues"
|
"url": "https://github.com/smart-chain-fr/leCoffre-back/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/smart-chain-fr/tezosLink#readme",
|
"homepage": "https://github.com/smart-chain-fr/leCoffre-back#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.6",
|
|
||||||
"@emotion/styled": "^11.10.6",
|
|
||||||
"@mui/material": "^5.11.12",
|
|
||||||
"@prisma/client": "^4.9.0",
|
"@prisma/client": "^4.9.0",
|
||||||
"apexcharts": "^3.36.3",
|
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
@ -53,20 +42,15 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"le-coffre-ressources": "github:smart-chain-fr/leCoffre-resources#v0.2",
|
||||||
"module-alias": "^2.2.2",
|
"module-alias": "^2.2.2",
|
||||||
"next": "^13.1.5",
|
"next": "^13.1.5",
|
||||||
"node-schedule": "^2.1.1",
|
"node-schedule": "^2.1.1",
|
||||||
"prisma": "^4.9.0",
|
"prisma": "^4.9.0",
|
||||||
"prisma-query": "^2.0.0",
|
"prisma-query": "^2.0.0",
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-apexcharts": "^1.4.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-tsparticles": "^2.8.0",
|
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"sass": "^1.58.0",
|
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"tsparticles": "^2.8.0",
|
|
||||||
"typedi": "^0.10.0",
|
"typedi": "^0.10.0",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
@ -76,8 +60,6 @@
|
|||||||
"@types/express": "^4.17.16",
|
"@types/express": "^4.17.16",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
"@types/node-schedule": "^2.1.0",
|
"@types/node-schedule": "^2.1.0",
|
||||||
"@types/react": "^18.0.27",
|
|
||||||
"@types/react-dom": "^18.0.10",
|
|
||||||
"@types/uuid": "^9.0.0",
|
"@types/uuid": "^9.0.0",
|
||||||
"nodemon": "^2.0.20",
|
"nodemon": "^2.0.20",
|
||||||
"prettier": "2.8.4"
|
"prettier": "2.8.4"
|
||||||
|
271
public/bg.svg
Before Width: | Height: | Size: 2.9 MiB |
Before Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 99 KiB |
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Tezos Link",
|
|
||||||
"name": "Your gateway to the Tezos network",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/favicon.ico",
|
|
||||||
"sizes": "32x32 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "dark",
|
|
||||||
"background_color": "dark"
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ import { Controller, Get, Post } from "@ControllerPattern/index";
|
|||||||
import { Service } from "typedi";
|
import { Service } from "typedi";
|
||||||
import { ProjectEntity } from "@Common/ressources";
|
import { ProjectEntity } from "@Common/ressources";
|
||||||
import { IsNotEmpty, IsString, IsUUID, validateOrReject } from "class-validator";
|
import { IsNotEmpty, IsString, IsUUID, validateOrReject } from "class-validator";
|
||||||
import ProjectService from "@Services/project/ProjectsService";
|
import ProjectService from "@Services/_TemplateService/_TemplateService";
|
||||||
import ObjectHydrate from "@Common/helpers/ObjectHydrate";
|
import ObjectHydrate from "@Common/helpers/ObjectHydrate";
|
||||||
import ApiController from "@Common/system/controller-pattern/ApiController";
|
import ApiController from "@Common/system/controller-pattern/ApiController";
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ export default class ProjectController extends ApiController {
|
|||||||
@Get("/projects")
|
@Get("/projects")
|
||||||
protected async get(req: Request, res: Response) {
|
protected async get(req: Request, res: Response) {
|
||||||
// const query = processFindManyQuery(req.query);
|
// const query = processFindManyQuery(req.query);
|
||||||
// this.httpSuccess(res, await this.projectService.getByCriterias(query));
|
// this.httpSuccess(res, await this.projectService.getByCrite@rias(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("/projects/:uuid")
|
@Get("/projects/:uuid")
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
import { Service } from "typedi";
|
|
||||||
import IConfig from "./IConfig";
|
|
||||||
import schedule from "node-schedule";
|
|
||||||
import FunctionBinder, { IFunctionBinder } from "@Common/helpers/FunctionBinder";
|
|
||||||
|
|
||||||
type ICronTimer = {
|
|
||||||
second?: string | "*" | null;
|
|
||||||
minute?: string | "*" | null;
|
|
||||||
hour?: string | "*" | null;
|
|
||||||
dayMonth?: string | "*" | null;
|
|
||||||
month?: string | "*" | null;
|
|
||||||
dayWeek?: string | "*" | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export default class Cron {
|
|
||||||
private readonly cronJobs: schedule.Job[] = [];
|
|
||||||
private static readonly runningJobs: boolean[] = [];
|
|
||||||
public async run(CronConfig: IConfig) {
|
|
||||||
this.cronJobs.splice(0);
|
|
||||||
this.cancel();
|
|
||||||
this.cronJobs.push(...CronConfig.jobs.filter((job) => job.enabled).map((job, index) => this.buildCronJob(job, index, CronConfig.binders)));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public cancel() {
|
|
||||||
this.cronJobs.forEach((job) => job.cancel());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createTimer(cronTimer: ICronTimer) {
|
|
||||||
return [cronTimer.second ?? "*", cronTimer.minute ?? "*", cronTimer.hour ?? "*", cronTimer.dayMonth ?? "*", cronTimer.month ?? "*", cronTimer.dayWeek ?? "*"].join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildCronJob(job: IConfig["jobs"][number], index: number, binders: IFunctionBinder[]) {
|
|
||||||
return schedule.scheduleJob(job.cronTime, () => Cron.scheduleJob(job, job.onTick, index, binders));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Prevent same jobs superposition
|
|
||||||
*/
|
|
||||||
private static async scheduleJob(jobConfig: IConfig["jobs"][number], cronCommand: () => Promise<any>, index: number, binders: IFunctionBinder[]) {
|
|
||||||
if (Cron.runningJobs[index]) return;
|
|
||||||
Cron.runningJobs[index] = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.info(`${Cron.getDate()} CronJob: ${jobConfig.cronTime} ${jobConfig.name}: started`);
|
|
||||||
|
|
||||||
if (binders.length) {
|
|
||||||
await FunctionBinder.bind(cronCommand, binders);
|
|
||||||
} else {
|
|
||||||
await cronCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.info(`${Cron.getDate()} CronJob: ${jobConfig.name}: end success`);
|
|
||||||
} catch (e) {
|
|
||||||
console.info(`${Cron.getDate()} CronJob: ${jobConfig.name}: end with error`);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
Cron.runningJobs[index] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static getDate() {
|
|
||||||
const d = new Date();
|
|
||||||
return `${d.getDate()}/${d.getMonth() + 1}/${d.getFullYear()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
import { IFunctionBinder } from "@Common/helpers/FunctionBinder";
|
|
||||||
|
|
||||||
export default interface IConfig {
|
|
||||||
binders: IFunctionBinder[];
|
|
||||||
jobs: {
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
cronTime: string | Date;
|
|
||||||
onTick: () => Promise<any>;
|
|
||||||
enabled?: boolean;
|
|
||||||
}[];
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
import IConfig from "@Common/cron/IConfig";
|
|
||||||
import Cron from "@Common/cron/Cron";
|
|
||||||
export type { IConfig };
|
|
||||||
export default Cron;
|
|
@ -1,34 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "app_projects" (
|
|
||||||
"id" SERIAL NOT NULL,
|
|
||||||
"title" VARCHAR(255) NOT NULL,
|
|
||||||
"uuid" VARCHAR(255) NOT NULL,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
"network" VARCHAR(255) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "app_projects_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "app_metrics" (
|
|
||||||
"id" SERIAL NOT NULL,
|
|
||||||
"path" VARCHAR(255) NOT NULL,
|
|
||||||
"uuid" VARCHAR(255) NOT NULL,
|
|
||||||
"remote_address" VARCHAR(255) NOT NULL,
|
|
||||||
"date_requested" TIMESTAMP(3) NOT NULL,
|
|
||||||
"projectId" INTEGER NOT NULL,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "app_metrics_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "app_projects_uuid_key" ON "app_projects"("uuid");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "app_metrics_uuid_key" ON "app_metrics"("uuid");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "app_metrics" ADD CONSTRAINT "app_metrics_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "app_projects"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -1,2 +0,0 @@
|
|||||||
-- DropIndex
|
|
||||||
DROP INDEX "app_metrics_uuid_key";
|
|
@ -1,2 +0,0 @@
|
|||||||
-- AlterTable
|
|
||||||
ALTER TABLE "app_metrics" ALTER COLUMN "date_requested" SET DATA TYPE DATE;
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
Warnings:
|
|
||||||
|
|
||||||
- The primary key for the `app_metrics` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
|
||||||
- You are about to drop the column `id` on the `app_metrics` table. All the data in the column will be lost.
|
|
||||||
- You are about to drop the column `projectId` on the `app_metrics` table. All the data in the column will be lost.
|
|
||||||
- The primary key for the `app_projects` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
|
||||||
- You are about to drop the column `id` on the `app_projects` table. All the data in the column will be lost.
|
|
||||||
- A unique constraint covering the columns `[uuid]` on the table `app_metrics` will be added. If there are existing duplicate values, this will fail.
|
|
||||||
- Added the required column `projectUuid` to the `app_metrics` table without a default value. This is not possible if the table is not empty.
|
|
||||||
|
|
||||||
*/
|
|
||||||
-- DropForeignKey
|
|
||||||
ALTER TABLE "app_metrics" DROP CONSTRAINT "app_metrics_projectId_fkey";
|
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "app_metrics" DROP CONSTRAINT "app_metrics_pkey",
|
|
||||||
DROP COLUMN "id",
|
|
||||||
DROP COLUMN "projectId",
|
|
||||||
ADD COLUMN "projectUuid" TEXT NOT NULL,
|
|
||||||
ADD CONSTRAINT "app_metrics_pkey" PRIMARY KEY ("uuid");
|
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "app_projects" DROP CONSTRAINT "app_projects_pkey",
|
|
||||||
DROP COLUMN "id",
|
|
||||||
ADD CONSTRAINT "app_projects_pkey" PRIMARY KEY ("uuid");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "app_metrics_uuid_key" ON "app_metrics"("uuid");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "app_metrics" ADD CONSTRAINT "app_metrics_projectUuid_fkey" FOREIGN KEY ("projectUuid") REFERENCES "app_projects"("uuid") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -35,7 +35,7 @@ model Contacts {
|
|||||||
last_name String @db.VarChar(255)
|
last_name String @db.VarChar(255)
|
||||||
email String @db.VarChar(255)
|
email String @db.VarChar(255)
|
||||||
phone_number String? @db.VarChar(50)
|
phone_number String? @db.VarChar(50)
|
||||||
cell_phone_number String @db.VarChar(50)
|
cell_phone_number String? @db.VarChar(50)
|
||||||
civility ECivility @default(MALE)
|
civility ECivility @default(MALE)
|
||||||
address Addresses @relation(fields: [address_uuid], references: [uuid])
|
address Addresses @relation(fields: [address_uuid], references: [uuid])
|
||||||
address_uuid String @unique @db.VarChar(255)
|
address_uuid String @unique @db.VarChar(255)
|
||||||
@ -48,16 +48,16 @@ model Contacts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Users {
|
model Users {
|
||||||
uuid String @id @unique @default(uuid())
|
uuid String @id @unique @default(uuid())
|
||||||
idNot String @unique @db.VarChar(255)
|
idNot String @unique @db.VarChar(255)
|
||||||
contact Contacts @relation(fields: [contact_uuid], references: [uuid])
|
contact Contacts @relation(fields: [contact_uuid], references: [uuid])
|
||||||
contact_uuid String @unique @db.VarChar(255)
|
contact_uuid String @unique @db.VarChar(255)
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
office_membership Office @relation(fields: [office_uuid], references: [uuid])
|
office_membership Office @relation(fields: [office_uuid], references: [uuid])
|
||||||
office_uuid String @db.VarChar(255)
|
office_uuid String @db.VarChar(255)
|
||||||
user_has_notifications UserHasNotifications[]
|
user_has_notifications UserHasNotifications[]
|
||||||
office_folder_has_stakeholder OfficeFolderHasStakeholder[]
|
office_folder_has_stakeholder OfficeFolderHasStakeholders[]
|
||||||
|
|
||||||
@@map("users")
|
@@map("users")
|
||||||
}
|
}
|
||||||
@ -117,20 +117,20 @@ model Notifications {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model OfficeFolders {
|
model OfficeFolders {
|
||||||
uuid String @id @unique @default(uuid())
|
uuid String @id @unique @default(uuid())
|
||||||
folder_number String @db.VarChar(255)
|
folder_number String @db.VarChar(255)
|
||||||
name String @db.VarChar(255)
|
name String @db.VarChar(255)
|
||||||
description String? @db.VarChar(255)
|
description String? @db.VarChar(255)
|
||||||
archived_description String? @db.VarChar(255)
|
archived_description String? @db.VarChar(255)
|
||||||
status EFolderStatus @default(LIVE)
|
status EFolderStatus @default(LIVE)
|
||||||
deed Deed @relation(fields: [deed_uuid], references: [uuid])
|
deed Deed @relation(fields: [deed_uuid], references: [uuid])
|
||||||
deed_uuid String @unique @db.VarChar(255)
|
deed_uuid String @unique @db.VarChar(255)
|
||||||
office Office @relation(fields: [office_uuid], references: [uuid])
|
office Office @relation(fields: [office_uuid], references: [uuid])
|
||||||
office_uuid String @db.VarChar(255)
|
office_uuid String @db.VarChar(255)
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
office_folder_has_customers OfficeFolderHasCustomers[]
|
office_folder_has_customers OfficeFolderHasCustomers[]
|
||||||
office_folder_has_stakeholder OfficeFolderHasStakeholder[]
|
office_folder_has_stakeholder OfficeFolderHasStakeholders[]
|
||||||
documents Documents[]
|
documents Documents[]
|
||||||
|
|
||||||
@@map("office_folders")
|
@@map("office_folders")
|
||||||
@ -148,7 +148,7 @@ model OfficeFolderHasCustomers {
|
|||||||
@@map("office_folder_has_customers")
|
@@map("office_folder_has_customers")
|
||||||
}
|
}
|
||||||
|
|
||||||
model OfficeFolderHasStakeholder {
|
model OfficeFolderHasStakeholders {
|
||||||
uuid String @id @unique @default(uuid())
|
uuid String @id @unique @default(uuid())
|
||||||
office_folder OfficeFolders @relation(fields: [office_folder_uuid], references: [uuid])
|
office_folder OfficeFolders @relation(fields: [office_folder_uuid], references: [uuid])
|
||||||
office_folder_uuid String @db.VarChar(255)
|
office_folder_uuid String @db.VarChar(255)
|
||||||
@ -163,7 +163,7 @@ model OfficeFolderHasStakeholder {
|
|||||||
model Documents {
|
model Documents {
|
||||||
uuid String @id @unique @default(uuid())
|
uuid String @id @unique @default(uuid())
|
||||||
document_status EDocumentStatus @default(ASKED)
|
document_status EDocumentStatus @default(ASKED)
|
||||||
type DocumentTypes @relation(fields: [type_uuid], references: [uuid])
|
document_type DocumentTypes @relation(fields: [type_uuid], references: [uuid])
|
||||||
type_uuid String @db.VarChar(255)
|
type_uuid String @db.VarChar(255)
|
||||||
blockchain_anchor BlockchainAnchors? @relation(fields: [blockchain_anchor_uuid], references: [uuid])
|
blockchain_anchor BlockchainAnchors? @relation(fields: [blockchain_anchor_uuid], references: [uuid])
|
||||||
blockchain_anchor_uuid String? @db.VarChar(255)
|
blockchain_anchor_uuid String? @db.VarChar(255)
|
||||||
@ -180,13 +180,13 @@ model Documents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model DocumentHistory {
|
model DocumentHistory {
|
||||||
uuid String @id @unique @default(uuid())
|
uuid String @id @unique @default(uuid())
|
||||||
document_history_type EDocumentStatus @default(ASKED)
|
document_status EDocumentStatus @default(ASKED)
|
||||||
refused_reason String? @db.VarChar(255)
|
refused_reason String? @db.VarChar(255)
|
||||||
document Documents @relation(fields: [document_uuid], references: [uuid])
|
document Documents @relation(fields: [document_uuid], references: [uuid])
|
||||||
document_uuid String @db.VarChar(255)
|
document_uuid String @db.VarChar(255)
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
|
|
||||||
@@map("document_history")
|
@@map("document_history")
|
||||||
}
|
}
|
||||||
@ -216,11 +216,11 @@ model DocumentTypes {
|
|||||||
uuid String @id @unique @default(uuid())
|
uuid String @id @unique @default(uuid())
|
||||||
name String @db.VarChar(255)
|
name String @db.VarChar(255)
|
||||||
public_description String @db.VarChar(255)
|
public_description String @db.VarChar(255)
|
||||||
private_description String @db.VarChar(255)
|
private_description String? @db.VarChar(255)
|
||||||
archived_at DateTime?
|
archived_at DateTime?
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
Documents Documents[]
|
documents Documents[]
|
||||||
deed_has_document_types DeedHasDocumentTypes[]
|
deed_has_document_types DeedHasDocumentTypes[]
|
||||||
deed_type_has_document_types DeedTypeHasDocumentTypes[]
|
deed_type_has_document_types DeedTypeHasDocumentTypes[]
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ model Deed {
|
|||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
deed_has_document_types DeedHasDocumentTypes[]
|
deed_has_document_types DeedHasDocumentTypes[]
|
||||||
office_folders OfficeFolders[]
|
office_folder OfficeFolders?
|
||||||
|
|
||||||
@@map("deed")
|
@@map("deed")
|
||||||
}
|
}
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
export type IFunctionBinder = (next?: IFunctionBinder) => Promise<void>;
|
|
||||||
/**
|
|
||||||
* @description execute functions on body of another function
|
|
||||||
* @example `
|
|
||||||
* const binders = [fooA, fooB, fooC];
|
|
||||||
*
|
|
||||||
* //Will be something like
|
|
||||||
* function fooA(fooB, fooC, next) {
|
|
||||||
* //code...
|
|
||||||
* fooB(fooC, next);
|
|
||||||
* //code...
|
|
||||||
* }
|
|
||||||
* function fooB(fooC, next) {
|
|
||||||
* //code...
|
|
||||||
* fooC(next);
|
|
||||||
* //code...
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* function fooC(next) {
|
|
||||||
* //code...
|
|
||||||
* next();
|
|
||||||
* //code...
|
|
||||||
* }
|
|
||||||
* `
|
|
||||||
* fooC will be executed in body of fooB and fooB in body of fooA
|
|
||||||
*/
|
|
||||||
export default abstract class FunctionBinder {
|
|
||||||
public static async bind(next: () => Promise<any>, binders: IFunctionBinder[]) {
|
|
||||||
let currentBinder = async function () {
|
|
||||||
await next();
|
|
||||||
};
|
|
||||||
|
|
||||||
let index = binders.length;
|
|
||||||
while (index--) {
|
|
||||||
const localIndex = index;
|
|
||||||
const nextBinder = currentBinder;
|
|
||||||
currentBinder = async () => binders[localIndex]!(nextBinder);
|
|
||||||
}
|
|
||||||
|
|
||||||
await currentBinder();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export function getUUIDFromPath(path: string, re: RegExp): string {
|
|
||||||
let uuid = "";
|
|
||||||
const matches = path.match(re);
|
|
||||||
if (matches !== null) {
|
|
||||||
uuid = matches[matches.length - 1]!;
|
|
||||||
}
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRPCFromPath(basePath: string, path: string, re: RegExp): string {
|
|
||||||
return path.replace("/" + basePath + getUUIDFromPath(path, re), "");
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
|||||||
|
// import Database from "@Common/databases/Database";
|
||||||
|
import { ORMBadQueryError } from "@Common/system/database/exceptions/ORMBadQueryError";
|
||||||
|
import BaseRepository from "@Repositories/BaseRepository";
|
||||||
|
import { Service } from "typedi";
|
||||||
|
|
||||||
|
export class RequestsByDayMetrics {
|
||||||
|
date_requested!: Date;
|
||||||
|
count!: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CountRpcPathUsage {
|
||||||
|
path!: string;
|
||||||
|
count!: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class _TemplateRepository extends BaseRepository {
|
||||||
|
// TODO ? -> Injection private database: Database
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
// protected get model() {
|
||||||
|
// return this.database.getClient().metric;
|
||||||
|
// }
|
||||||
|
// protected get instanceDb() {
|
||||||
|
// return this.database.getClient();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Find many T
|
||||||
|
* @arguments : query: Prisma.TFindManyArgs
|
||||||
|
* @returns : Promise<T[]>
|
||||||
|
*/
|
||||||
|
public async findMany() {
|
||||||
|
try {
|
||||||
|
// Query
|
||||||
|
} catch (error) {
|
||||||
|
throw new ORMBadQueryError((error as Error).message, error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Find many T
|
||||||
|
* @arguments : t : Partial<T>
|
||||||
|
* @returns : Promise<T>
|
||||||
|
*/
|
||||||
|
public async findOne() {
|
||||||
|
try {
|
||||||
|
// Query
|
||||||
|
} catch (error) {
|
||||||
|
throw new ORMBadQueryError((error as Error).message, error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Create T
|
||||||
|
* @arguments : t : Partial<T>
|
||||||
|
* @returns : Promise<T[]>
|
||||||
|
*/
|
||||||
|
public async create() {
|
||||||
|
try {
|
||||||
|
// Create a new T
|
||||||
|
// return T
|
||||||
|
} catch (error) {
|
||||||
|
throw new ORMBadQueryError((error as Error).message, error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Create bulk T
|
||||||
|
* @arguments : t : Partial<T[]>
|
||||||
|
* @returns : Promise<T[]>
|
||||||
|
*/
|
||||||
|
public async createMany() {
|
||||||
|
try {
|
||||||
|
// Create many new T
|
||||||
|
// return T[]
|
||||||
|
} catch (error) {
|
||||||
|
throw new ORMBadQueryError((error as Error).message, error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,220 +0,0 @@
|
|||||||
// import TezosLink from "@Common/databases/TezosLink";
|
|
||||||
// import ObjectHydrate from "@Common/helpers/ObjectHydrate";
|
|
||||||
// import { MetricEntity } from "@Common/ressources";
|
|
||||||
// import { ORMBadQueryError } from "@Common/system/database/exceptions/ORMBadQueryError";
|
|
||||||
// import { type Prisma } from "@prisma/client";
|
|
||||||
|
|
||||||
import { Service } from "typedi";
|
|
||||||
// import { v4 as uuidv4 } from "uuid";
|
|
||||||
import BaseRepository from "../BaseRepository";
|
|
||||||
|
|
||||||
export class RequestsByDayMetrics {
|
|
||||||
date_requested!: Date;
|
|
||||||
count!: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CountRpcPathUsage {
|
|
||||||
path!: string;
|
|
||||||
count!: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export default class MetricsRepository extends BaseRepository {
|
|
||||||
// constructor(private database: TezosLink) {
|
|
||||||
// super();
|
|
||||||
// }
|
|
||||||
// protected get model() {
|
|
||||||
// return this.database.getClient().metric;
|
|
||||||
// }
|
|
||||||
// protected get instanceDb() {
|
|
||||||
// return this.database.getClient();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public async findMany(query: Prisma.MetricFindManyArgs): Promise<MetricEntity[]> {
|
|
||||||
// try {
|
|
||||||
// // Use Math.min to limit the number of rows fetched
|
|
||||||
// const limit = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows);
|
|
||||||
|
|
||||||
// // Update the query with the limited limit
|
|
||||||
// const metrics = await this.model.findMany({ ...query, take: limit });
|
|
||||||
// return ObjectHydrate.map<MetricEntity>(MetricEntity, metrics, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public async findOne(metricEntity: Partial<MetricEntity>): Promise<Partial<MetricEntity> | null> {
|
|
||||||
// try {
|
|
||||||
// const metric = await this.model.findUnique({ where: metricEntity });
|
|
||||||
// return ObjectHydrate.hydrate<MetricEntity>(new MetricEntity(), metric, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public async create(metricEntity: Partial<MetricEntity>): Promise<MetricEntity> {
|
|
||||||
// try {
|
|
||||||
// const data = { ...metricEntity };
|
|
||||||
// data.uuid = uuidv4();
|
|
||||||
// const metric = (await this.model.create({
|
|
||||||
// data: {
|
|
||||||
// path: data.path!,
|
|
||||||
// uuid: data.uuid!,
|
|
||||||
// remote_address: data.remote_address!,
|
|
||||||
// date_requested: data.date_requested!,
|
|
||||||
// project: {
|
|
||||||
// connect: {
|
|
||||||
// uuid: data.project!.uuid!,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// })) as MetricEntity;
|
|
||||||
// return ObjectHydrate.hydrate<MetricEntity>(new MetricEntity(), metric, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Create many metrics in bulk
|
|
||||||
// public async createMany(metricEntity: Partial<MetricEntity[]>): Promise<MetricEntity[]> {
|
|
||||||
// try {
|
|
||||||
// const result: MetricEntity[] = [];
|
|
||||||
|
|
||||||
// this.instanceDb.$transaction(async (transaction: Prisma.TransactionClient) => {
|
|
||||||
// for (const item of metricEntity) {
|
|
||||||
// if (!item) continue;
|
|
||||||
// const data = { ...item };
|
|
||||||
// data.uuid = uuidv4();
|
|
||||||
// result.push(
|
|
||||||
// await transaction.metric.create({
|
|
||||||
// data: {
|
|
||||||
// path: data.path!,
|
|
||||||
// uuid: data.uuid!,
|
|
||||||
// remote_address: data.remote_address!,
|
|
||||||
// date_requested: data.date_requested!,
|
|
||||||
// project: {
|
|
||||||
// connect: {
|
|
||||||
// uuid: data.projectUuid!,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// return ObjectHydrate.map<MetricEntity>(MetricEntity, result, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Count Rpc path usage for a specific project
|
|
||||||
// public async countRpcPathUsage(ProjectUuid: string, from: Date, to: Date): Promise<CountRpcPathUsage[]> {
|
|
||||||
// try {
|
|
||||||
// const result: CountRpcPathUsage[] = [];
|
|
||||||
// const response = await this.model.groupBy({
|
|
||||||
// by: ["path"],
|
|
||||||
// _count: {
|
|
||||||
// path: true,
|
|
||||||
// },
|
|
||||||
// where: {
|
|
||||||
// projectUuid: ProjectUuid,
|
|
||||||
// date_requested: {
|
|
||||||
// gte: from,
|
|
||||||
// lte: to,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// response.forEach((item) => {
|
|
||||||
// result.push({
|
|
||||||
// path: item.path,
|
|
||||||
// count: item._count.path,
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// return ObjectHydrate.map<CountRpcPathUsage>(CountRpcPathUsage, response, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Last requests for a specific project
|
|
||||||
// public async findLastRequests(projectUuid: string, limit: number): Promise<MetricEntity[]> {
|
|
||||||
// try {
|
|
||||||
// // Use Math.min to limit the number of rows fetched
|
|
||||||
// const rows = Math.min(limit || this.defaultFetchRows, this.maxFetchRows);
|
|
||||||
// const metrics = await this.model.findMany({
|
|
||||||
// where: {
|
|
||||||
// projectUuid: projectUuid,
|
|
||||||
// },
|
|
||||||
// take: rows,
|
|
||||||
// orderBy: {
|
|
||||||
// date_requested: "desc",
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// return ObjectHydrate.map<MetricEntity>(MetricEntity, metrics, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Find Requests by Day for a specific project
|
|
||||||
// public async findRequestsByDay(projectUuid: string, from: Date, to: Date): Promise<RequestsByDayMetrics[]> {
|
|
||||||
// try {
|
|
||||||
// const result: RequestsByDayMetrics[] = [];
|
|
||||||
// const response = await this.model.groupBy({
|
|
||||||
// by: ["date_requested"],
|
|
||||||
// _count: {
|
|
||||||
// date_requested: true,
|
|
||||||
// },
|
|
||||||
// where: {
|
|
||||||
// projectUuid: projectUuid,
|
|
||||||
// date_requested: {
|
|
||||||
// gte: from,
|
|
||||||
// lte: to,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// response.forEach((item) => {
|
|
||||||
// result.push({
|
|
||||||
// date_requested: item.date_requested,
|
|
||||||
// count: item._count.date_requested,
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// return ObjectHydrate.map<RequestsByDayMetrics>(RequestsByDayMetrics, result, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Count all metrics by criterias for a specific project
|
|
||||||
// public async countAll(projectUuid: string): Promise<number> {
|
|
||||||
// try {
|
|
||||||
// return this.model.count({
|
|
||||||
// where: {
|
|
||||||
// projectUuid: projectUuid,
|
|
||||||
// },
|
|
||||||
// }) as Promise<number>;
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Remove Three months old metrics
|
|
||||||
// public async removeOldMetricsBymonths(months: number): Promise<void> {
|
|
||||||
// try {
|
|
||||||
// const date = new Date();
|
|
||||||
// date.setMonth(date.getMonth() - months);
|
|
||||||
// this.model.deleteMany({
|
|
||||||
// where: {
|
|
||||||
// date_requested: {
|
|
||||||
// lte: date,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
|||||||
// import TezosLink from "@Common/databases/TezosLink";
|
|
||||||
// import ObjectHydrate from "@Common/helpers/ObjectHydrate";
|
|
||||||
import { ProjectEntity } from "@Common/ressources";
|
|
||||||
// import { ORMBadQueryError } from "@Common/system/database/exceptions/ORMBadQueryError";
|
|
||||||
import { Service } from "typedi";
|
|
||||||
// import { v4 as uuidv4 } from "uuid";
|
|
||||||
import BaseRepository from "../BaseRepository";
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export default class ProjectsRepository extends BaseRepository {
|
|
||||||
// constructor(private database: TezosLink) {
|
|
||||||
// super();
|
|
||||||
// }
|
|
||||||
// protected get model() {
|
|
||||||
// return this.database.getClient().project;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public async findMany(query: Prisma.ProjectFindManyArgs): Promise<ProjectEntity[]> {
|
|
||||||
// try {
|
|
||||||
// // Use Math.min to limit the number of rows fetched
|
|
||||||
// const limit = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows);
|
|
||||||
|
|
||||||
// // Update the query with the limited limit
|
|
||||||
// const projects = await this.model.findMany({ ...query, take: limit });
|
|
||||||
// return ObjectHydrate.map<ProjectEntity>(ProjectEntity, projects, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
public async findOne(projectEntity: Partial<ProjectEntity>) {
|
|
||||||
// try {
|
|
||||||
// const project = (await this.model.findFirst({
|
|
||||||
// where: projectEntity,
|
|
||||||
// })) as ProjectEntity;
|
|
||||||
// return ObjectHydrate.hydrate<ProjectEntity>(new ProjectEntity(), project, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public async create(projectEntity: Partial<ProjectEntity>) {
|
|
||||||
// try {
|
|
||||||
// const data = { ...projectEntity };
|
|
||||||
// data.uuid = uuidv4();
|
|
||||||
// const project = (await this.model.create({
|
|
||||||
// data: {
|
|
||||||
// uuid: data.uuid,
|
|
||||||
// title: data.title!,
|
|
||||||
// network: data.network!,
|
|
||||||
// },
|
|
||||||
// include: {
|
|
||||||
// // Include metrics
|
|
||||||
// Metrics: true,
|
|
||||||
// },
|
|
||||||
// })) as ProjectEntity;
|
|
||||||
// return ObjectHydrate.hydrate<ProjectEntity>(new ProjectEntity(), project, { strategy: "exposeAll" });
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new ORMBadQueryError((error as Error).message, error as Error);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
|||||||
import ProjectEntity from "./ProjectEntity";
|
|
||||||
|
|
||||||
export default class MetricEntity {
|
|
||||||
public uuid!: string;
|
|
||||||
|
|
||||||
public path!: string;
|
|
||||||
|
|
||||||
public remote_address!: string;
|
|
||||||
|
|
||||||
public date_requested!: Date;
|
|
||||||
|
|
||||||
public projectUuid!: string;
|
|
||||||
|
|
||||||
public project!: ProjectEntity;
|
|
||||||
|
|
||||||
public createdAt?: Date;
|
|
||||||
|
|
||||||
public updatedAt?: Date;
|
|
||||||
|
|
||||||
set remoteAddress(remote_address: string) {
|
|
||||||
this.remote_address = remote_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
get remoteAddress() {
|
|
||||||
return this.remote_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
set dateRequested(date_requested: Date) {
|
|
||||||
this.date_requested = date_requested;
|
|
||||||
}
|
|
||||||
|
|
||||||
get dateRequested() {
|
|
||||||
return this.date_requested;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
|||||||
import { IsNotEmpty } from "class-validator";
|
|
||||||
import MetricEntity from "./MetricEntity";
|
|
||||||
|
|
||||||
export default class ProjectEntity {
|
|
||||||
public uuid!: string;
|
|
||||||
|
|
||||||
@IsNotEmpty({ groups: ["create"] })
|
|
||||||
public title!: string;
|
|
||||||
|
|
||||||
public createdAt!: Date;
|
|
||||||
|
|
||||||
public updatedAt!: Date;
|
|
||||||
|
|
||||||
@IsNotEmpty({ groups: ["create"] })
|
|
||||||
public network!: string;
|
|
||||||
|
|
||||||
public metrics?: MetricEntity[];
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
import ProjectEntity from "./ProjectEntity";
|
|
||||||
import MetricEntity from "./MetricEntity";
|
|
||||||
|
|
||||||
export { ProjectEntity, MetricEntity};
|
|
@ -1,5 +1,4 @@
|
|||||||
// import ProjectsRepository from "@Common/repositories/projects/ProjectsRepository";
|
// import ProjectsRepository from "@Common/repositories/projects/ProjectsRepository";
|
||||||
import { ProjectEntity } from "@Common/ressources";
|
|
||||||
import BaseService from "@Services/BaseService";
|
import BaseService from "@Services/BaseService";
|
||||||
import { Service } from "typedi";
|
import { Service } from "typedi";
|
||||||
|
|
||||||
@ -11,14 +10,19 @@ export default class ProjectsService extends BaseService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws {Error} If projects are undefined
|
* @throws {Error} If projects are undefined
|
||||||
|
* @returns : Promise<T[]>
|
||||||
|
* @param : query: ReturnType<typeof processFindManyQuery>
|
||||||
*/
|
*/
|
||||||
// public async getByCriterias(query: ReturnType<typeof processFindManyQuery>): Promise<ProjectEntity[]> {
|
public async getByCriterias() {
|
||||||
// return this.projectRepository.findMany(query);
|
// return this.projectRepository.findMany(query);
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws {Error} If project is undefined
|
* @throws {Error} If project is undefined
|
||||||
|
* @returns : Partial<T>
|
||||||
|
* @param : t : T
|
||||||
*/
|
*/
|
||||||
public async getByUUID(projectEntity: Partial<ProjectEntity>){
|
public async getByUUID() {
|
||||||
// const project = await this.projectRepository.findOne(projectEntity);
|
// const project = await this.projectRepository.findOne(projectEntity);
|
||||||
// if (!project) Promise.reject(new Error("Cannot get project by uuid"));
|
// if (!project) Promise.reject(new Error("Cannot get project by uuid"));
|
||||||
// return project;
|
// return project;
|
||||||
@ -26,12 +30,12 @@ export default class ProjectsService extends BaseService {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @throws {Error} If project cannot be created
|
* @throws {Error} If project cannot be created
|
||||||
* @returns
|
* @returns : T
|
||||||
|
* @param : projectEntity: Partial<ProjectEntity>
|
||||||
*/
|
*/
|
||||||
public async create(projectEntity: Partial<ProjectEntity>){
|
public async create() {
|
||||||
// const project = await this.projectRepository.create(projectEntity);
|
// const project = await this.projectRepository.create(projectEntity);
|
||||||
// if (!project) Promise.reject(new Error("Cannot create project"));
|
// if (!project) Promise.reject(new Error("Cannot create project"));
|
||||||
// return project;
|
// return project;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,76 +0,0 @@
|
|||||||
import BaseService from "@Services/BaseService";
|
|
||||||
import { Service } from "typedi";
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export default class MetricsService extends BaseService {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @throws {Error} If metrics are undefined
|
|
||||||
// */
|
|
||||||
// public async getByCriterias(query: ReturnType<typeof processFindManyQuery>): Promise<MetricEntity[]> {
|
|
||||||
// return await this.metricRepository.findMany(query);
|
|
||||||
// }
|
|
||||||
// /**
|
|
||||||
// *
|
|
||||||
// * @throws {Error} If metric cannot be created
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// public async create(metricEntity: Partial<MetricEntity>): Promise<Partial<MetricEntity>> {
|
|
||||||
// const metric = await this.metricRepository.create(metricEntity);
|
|
||||||
// if (!metric) return Promise.reject(new Error("Cannot create metric"));
|
|
||||||
// return metric;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {Error} If metric is undefined
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
// public async getCountRpcPath(uuid: string, from: Date, to: Date): Promise<CountRpcPathUsage[]> {
|
|
||||||
// const pathsCount = await this.metricRepository.countRpcPathUsage(uuid, from, to);
|
|
||||||
// if (!pathsCount) return Promise.reject(new Error("Cannot get count of rpc path"));
|
|
||||||
// return pathsCount;
|
|
||||||
// }
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {Error} If metric is undefined
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
// public async getCountAllMetrics(metricEntity: Partial<MetricEntity>): Promise<number> {
|
|
||||||
// const count = await this.metricRepository.countAll(metricEntity.uuid!);
|
|
||||||
// if (isNaN(count)) Promise.reject(new Error("Cannot get count of metrics"));
|
|
||||||
// return count;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {Error} If metric is undefined
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
// public async getLastMetrics(uuid: string, limit: number): Promise<MetricEntity[]> {
|
|
||||||
// return await this.metricRepository.findLastRequests(uuid, limit);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {Error} If metric is undefined
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
// public async getRequestsByDay(uuid: string, from: Date, to: Date): Promise<RequestsByDayMetrics[]> {
|
|
||||||
// return await this.metricRepository.findRequestsByDay(uuid, from, to);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {Error} If metric is undefined
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
// public async removeThreeMontsOldMetrics(): Promise<void> {
|
|
||||||
// const months = 3;
|
|
||||||
// await this.metricRepository.removeOldMetricsBymonths(months);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
|||||||
import MetricEntity from "@Common/ressources/MetricEntity";
|
|
||||||
import HttpCodes from "@Common/system/controller-pattern/HttpCodes";
|
|
||||||
import { IHttpReponse, IStatusNode } from "@Common/system/interfaces/Interfaces";
|
|
||||||
import BaseService from "@Services/BaseService";
|
|
||||||
import axios from "axios";
|
|
||||||
import { IsNotEmpty, IsUUID, Validate } from "class-validator";
|
|
||||||
import { Service } from "typedi";
|
|
||||||
import IsRpcPathAllowed from "./validators/IsRpcPathAllowed";
|
|
||||||
import IsValidProject from "./validators/IsValidProject";
|
|
||||||
import { BackendVariables } from "@Common/config/variables/Variables";
|
|
||||||
import ProjectsRepository from "@Common/repositories/projects/ProjectsRepository";
|
|
||||||
|
|
||||||
export class RpcRequest {
|
|
||||||
@IsNotEmpty()
|
|
||||||
@Validate(IsRpcPathAllowed)
|
|
||||||
path!: string;
|
|
||||||
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsUUID()
|
|
||||||
@Validate(IsValidProject, [{ network: BaseService.network }])
|
|
||||||
uuid!: string;
|
|
||||||
|
|
||||||
@IsNotEmpty()
|
|
||||||
remoteAddress!: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export default class ProxyService extends BaseService {
|
|
||||||
constructor(private projectRepository: ProjectsRepository, private variables: BackendVariables) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @throws {Error} if url is undefined
|
|
||||||
*/
|
|
||||||
public getHttpServerResponse(): IHttpReponse {
|
|
||||||
return {
|
|
||||||
status: HttpCodes.SUCCESS,
|
|
||||||
reason: null,
|
|
||||||
} as IHttpReponse;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @throws {Error} if url is undefined
|
|
||||||
*/
|
|
||||||
public async getNodesStatus(): Promise<IStatusNode> {
|
|
||||||
const archiveTestURL = new URL(`${this.variables.ARCHIVE_NODES_URL}/chains/main/blocks/head`);
|
|
||||||
const rollingTestURL = new URL(`${this.variables.ROLLING_NODES_URL}/chains/main/blocks/head`);
|
|
||||||
|
|
||||||
const archive_node = {
|
|
||||||
status: HttpCodes.INTERNAL_ERROR,
|
|
||||||
reason: null,
|
|
||||||
} as IHttpReponse;
|
|
||||||
const rolling_node = {
|
|
||||||
status: HttpCodes.INTERNAL_ERROR,
|
|
||||||
reason: null,
|
|
||||||
} as IHttpReponse;
|
|
||||||
|
|
||||||
const [archive, rolling] = await Promise.allSettled([axios.get(archiveTestURL.toString()), axios.get(rollingTestURL.toString())]);
|
|
||||||
|
|
||||||
if (archive.status === "fulfilled") archive_node.status = archive.value.status;
|
|
||||||
if (archive.status === "rejected") archive_node.reason = archive.reason;
|
|
||||||
|
|
||||||
if (rolling.status === "fulfilled") rolling_node.status = rolling.value.status;
|
|
||||||
if (rolling.status === "rejected") rolling_node.reason = rolling.reason;
|
|
||||||
|
|
||||||
return {
|
|
||||||
archive_node,
|
|
||||||
rolling_node,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {Error} If metric cannot be created
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
// async saveMetric(metricEntity: Partial<MetricEntity>) {
|
|
||||||
// const metric = await this.metricsRepository.create(metricEntity);
|
|
||||||
// if (!metric) return null;
|
|
||||||
// return metric;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Proxy proxy an http request to the right repositories
|
|
||||||
public async proxy(request: RpcRequest): Promise<string> {
|
|
||||||
console.info(`Received proxy request for path: ${request.path}`);
|
|
||||||
|
|
||||||
const project = await this.projectRepository.findOne({ uuid: request.uuid, network: BaseService.network });
|
|
||||||
|
|
||||||
// if (!project) {
|
|
||||||
// return Promise.reject(`Project uuid: ${request.uuid} with network: ${BaseService.network} does not exist`);
|
|
||||||
// }
|
|
||||||
|
|
||||||
let response = "";
|
|
||||||
|
|
||||||
if (this.isRollingNodeRedirection(request.path)) {
|
|
||||||
console.info("Forwarding request directly to rolling node (as a reverse proxy)");
|
|
||||||
const rollingURL = new URL(`${this.variables.ROLLING_NODES_URL}/${request.path}`);
|
|
||||||
const { data } = await axios.get(rollingURL.toString());
|
|
||||||
response = data;
|
|
||||||
} else {
|
|
||||||
console.info("Forwarding request directly to archive node (as a reverse proxy)");
|
|
||||||
const archiveURL = new URL(`${this.variables.ARCHIVE_NODES_URL}/${request.path}`);
|
|
||||||
const { data } = await axios.get(archiveURL.toString());
|
|
||||||
response = data;
|
|
||||||
}
|
|
||||||
// Logger les metrics
|
|
||||||
const metric = new MetricEntity();
|
|
||||||
Object.assign(metric, request, { project, dateRequested: new Date() });
|
|
||||||
|
|
||||||
// await this.saveMetric(metric);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
isRollingNodeRedirection(url: string): boolean {
|
|
||||||
const pureUrl = `/${url!.trim()}`;
|
|
||||||
console.info(`Checking if ${pureUrl} is a rolling node redirection`);
|
|
||||||
return Boolean(BaseService.rollingPatterns.find((rollingpattern) => pureUrl.includes(rollingpattern)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
|||||||
import BaseService from "@Services/BaseService";
|
|
||||||
import { ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
|
|
||||||
|
|
||||||
@ValidatorConstraint({ name: "IsRpcPathAllowed" })
|
|
||||||
export default class IsRpcPathAllowed implements ValidatorConstraintInterface {
|
|
||||||
public validate(path: string) {
|
|
||||||
return isAllowed(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public defaultMessage() {
|
|
||||||
return `not a valid path!`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAllowed(path: string): boolean {
|
|
||||||
const pureUrl = `/${path!.trim()}`;
|
|
||||||
let nonWhitelistedPart = "";
|
|
||||||
for (const whitelistPath of BaseService.whitelisted) {
|
|
||||||
if (pureUrl.includes(whitelistPath)) {
|
|
||||||
nonWhitelistedPart = pureUrl.slice(pureUrl.indexOf(whitelistPath) + whitelistPath.length);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const blacklistPath of BaseService.blacklisted) {
|
|
||||||
if (nonWhitelistedPart.includes(blacklistPath) || pureUrl.includes(blacklistPath)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
import BaseService from "@Services/BaseService";
|
|
||||||
import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
|
|
||||||
import Container from "typedi";
|
|
||||||
import { RpcRequest } from "../ProxyService";
|
|
||||||
import ProjectsRepository from "@Common/repositories/projects/ProjectsRepository";
|
|
||||||
|
|
||||||
@ValidatorConstraint({ name: "IsValidProject" })
|
|
||||||
export default class IsValidProject implements ValidatorConstraintInterface {
|
|
||||||
public async validate(uuid: string, args: ValidationArguments) {
|
|
||||||
const projectRepository = Container.get(ProjectsRepository);
|
|
||||||
|
|
||||||
if (args.constraints?.[0]?.network) {
|
|
||||||
return Boolean(await projectRepository.findOne({ uuid, network: BaseService.network }));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public defaultMessage(args: ValidationArguments) {
|
|
||||||
const network = args.constraints?.[0]!.network;
|
|
||||||
const uuid = (args.object as RpcRequest).uuid;
|
|
||||||
return `Project uuid: ${uuid} with network: ${network} does not exist`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
export interface IHttpReponse {
|
|
||||||
status: number;
|
|
||||||
reason: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IStatusNode {
|
|
||||||
archive_node: IHttpReponse;
|
|
||||||
rolling_node: IHttpReponse;
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
import "module-alias/register";
|
|
||||||
import "reflect-metadata";
|
|
||||||
import { Container } from "typedi";
|
|
||||||
import NextServer from "@Common/system/NextJs";
|
|
||||||
import dotenv from "dotenv";
|
|
||||||
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
dotenv.config();
|
|
||||||
const frontVariables = Container.get(FrontendVariables);
|
|
||||||
|
|
||||||
const port = frontVariables.WEB_PORT;
|
|
||||||
const rootUrl = frontVariables.WEB_ROOT_URL;
|
|
||||||
const label = frontVariables.WEB_LABEL ?? "Unknown Service";
|
|
||||||
|
|
||||||
Container.get(NextServer).init({
|
|
||||||
label,
|
|
||||||
isDev: frontVariables.NODE_ENV !== "production",
|
|
||||||
port: parseInt(port),
|
|
||||||
rootUrl,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
|||||||
export enum ContentType {
|
|
||||||
JSON = "application/json",
|
|
||||||
FORM_DATA = "multipart/form-data;",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default abstract class BaseApiService {
|
|
||||||
protected readonly backUrl =
|
|
||||||
process.env["NEXT_PUBLIC_API_HOSTNAME"] + ":" + process.env["NEXT_PUBLIC_API_PORT"] + process.env["NEXT_PUBLIC_API_ROOT_URL"];
|
|
||||||
protected readonly proxyUrl =
|
|
||||||
process.env["NEXT_PUBLIC_RPC_GATEWAY_HOSTNAME"] +
|
|
||||||
":" +
|
|
||||||
process.env["NEXT_PUBLIC_RPC_GATEWAY_PORT"] +
|
|
||||||
process.env["NEXT_PUBLIC_RPC_GATEWAY_ROOT_URL"];
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
protected constructor() {}
|
|
||||||
|
|
||||||
protected buildHeaders(contentType: ContentType) {
|
|
||||||
const headers = new Headers();
|
|
||||||
|
|
||||||
if (contentType === ContentType.JSON) {
|
|
||||||
headers.set("Content-Type", contentType);
|
|
||||||
}
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildBody(body: { [key: string]: unknown }): string {
|
|
||||||
return JSON.stringify(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async getRequest<T>(url: URL) {
|
|
||||||
const request = async () =>
|
|
||||||
await fetch(url, {
|
|
||||||
method: "GET",
|
|
||||||
headers: this.buildHeaders(ContentType.JSON),
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.sendRequest<T>(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async postRequest<T>(url: URL, body: { [key: string]: unknown } = {}) {
|
|
||||||
const request = async () =>
|
|
||||||
await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: this.buildHeaders(ContentType.JSON),
|
|
||||||
body: this.buildBody(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.sendRequest<T>(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async putRequest<T>(url: URL, body: { [key: string]: unknown } = {}) {
|
|
||||||
const request = async () =>
|
|
||||||
await fetch(url, {
|
|
||||||
method: "PUT",
|
|
||||||
headers: this.buildHeaders(ContentType.JSON),
|
|
||||||
body: this.buildBody(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.sendRequest<T>(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async patchRequest<T>(url: URL, body: { [key: string]: unknown } = {}) {
|
|
||||||
const request = async () =>
|
|
||||||
await fetch(url, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: this.buildHeaders(ContentType.JSON),
|
|
||||||
body: this.buildBody(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.sendRequest<T>(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async deleteRequest<T>(url: URL, body: { [key: string]: unknown } = {}) {
|
|
||||||
const request = async () =>
|
|
||||||
await fetch(url, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: this.buildHeaders(ContentType.JSON),
|
|
||||||
body: this.buildBody(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.sendRequest<T>(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async putFormDataRequest<T>(url: URL, body: FormData) {
|
|
||||||
const request = async () =>
|
|
||||||
await fetch(url, {
|
|
||||||
method: "PUT",
|
|
||||||
headers: this.buildHeaders(ContentType.FORM_DATA),
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.sendRequest<T>(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async sendRequest<T>(request: () => Promise<Response>): Promise<T> {
|
|
||||||
const response = await request();
|
|
||||||
return this.processResponse<T>(response, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async processResponse<T>(response: Response, request: () => Promise<Response>): Promise<T> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
let responseJson: any | null;
|
|
||||||
try {
|
|
||||||
responseJson = await response.json();
|
|
||||||
} catch (err: unknown) {
|
|
||||||
responseJson = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
return Promise.reject(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
return responseJson as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected onError(error: unknown) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IResponse {
|
|
||||||
http_status: number;
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
import BaseApiService from "src/front/Api/BaseApiService";
|
|
||||||
import { IProject } from "src/front/interfaces";
|
|
||||||
import { Service } from "typedi";
|
|
||||||
|
|
||||||
type IPostProject = {
|
|
||||||
title: string;
|
|
||||||
network: string;
|
|
||||||
};
|
|
||||||
@Service()
|
|
||||||
export default class Project extends BaseApiService {
|
|
||||||
private static instance: Project;
|
|
||||||
private readonly baseURl = this.backUrl.concat("/projects");
|
|
||||||
|
|
||||||
private constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getInstance() {
|
|
||||||
if (!this.instance) {
|
|
||||||
return new Project();
|
|
||||||
} else {
|
|
||||||
return this.instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getAllProject(): Promise<IProject[]> {
|
|
||||||
const url = new URL(this.baseURl);
|
|
||||||
try {
|
|
||||||
return await this.getRequest<IProject[]>(url);
|
|
||||||
} catch (err) {
|
|
||||||
this.onError(err);
|
|
||||||
return Promise.reject(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getOneProject(uuid: string): Promise<IProject> {
|
|
||||||
const url = new URL(this.baseURl.concat("/").concat(uuid));
|
|
||||||
try {
|
|
||||||
return await this.getRequest<IProject>(url);
|
|
||||||
} catch (err) {
|
|
||||||
this.onError(err);
|
|
||||||
return Promise.reject(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async postProject(params: IPostProject): Promise<IProject> {
|
|
||||||
const url = new URL(this.baseURl);
|
|
||||||
try {
|
|
||||||
return await this.postRequest<IProject>(url, params);
|
|
||||||
} catch (err) {
|
|
||||||
this.onError(err);
|
|
||||||
return Promise.reject(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
# Documentation
|
|
||||||
|
|
||||||
This is the documentation served at `https://<BASE_PATH>/documentation`
|
|
@ -1,69 +0,0 @@
|
|||||||
## Usage
|
|
||||||
|
|
||||||
Once sign up, a `Project ID` is generated for your project, to use within your app to make requests to tezoslink.net.
|
|
||||||
|
|
||||||
Then, add the tezoslink.net RPC endpoint to your prefered Tezos JS library.
|
|
||||||
|
|
||||||
> i.e with [Sotez](https://github.com/AndrewKishino/sotez) :
|
|
||||||
>
|
|
||||||
> ```js
|
|
||||||
> const sotez = new Sotez(
|
|
||||||
> "https://<NETWORK>.tezoslink.net/v1/<YOUR_PROJECT_ID>"
|
|
||||||
> );
|
|
||||||
> ```
|
|
||||||
|
|
||||||
## Networks
|
|
||||||
|
|
||||||
Use one of these endpoints as your Tezos client provider.
|
|
||||||
|
|
||||||
| NETWORK | DESCRIPTION | URL |
|
|
||||||
| ------- | ----------- | -------------------------------------------------- |
|
|
||||||
| Mainnet | JSON/RPC | https://mainnet.tezoslink.net/v1/<YOUR_PROJECT_ID> |
|
|
||||||
| Testnet | JSON/RPC | https://testnet.tezoslink.net/v1/<YOUR_PROJECT_ID> |
|
|
||||||
|
|
||||||
## Make requests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Be sure to replace YOUR-PROJECT-ID with a Project ID from your Tezos Link dashboard
|
|
||||||
$ curl https://mainnet.tezoslink.net/v1/<YOUR_PROJECT_ID>/chains/main/blocks/head
|
|
||||||
```
|
|
||||||
|
|
||||||
You should receive the last received block.
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
The `Project ID` authorize requests.
|
|
||||||
|
|
||||||
## RPC Endpoints
|
|
||||||
|
|
||||||
### Whitelisted
|
|
||||||
|
|
||||||
All requests of type `/chains/main/blocks(.*?)` are accepted.
|
|
||||||
|
|
||||||
> Example of valid paths:
|
|
||||||
>
|
|
||||||
> - `/chains/main/blocks/head/context/contracts/<ADDRESS>/balance`
|
|
||||||
> - `/chains/main/blocks/head/context/contracts/<ADDRESS>/delegate`
|
|
||||||
> - `/chains/main/blocks/head/context/contracts/<ADDRESS>/manager_key`
|
|
||||||
> - `/chains/main/blocks/head/context/contracts/<ADDRESS>/counter`
|
|
||||||
> - `/chains/main/blocks/head/context/delegates/<ADDRESS>`
|
|
||||||
> - `/chains/main/blocks/head/header`
|
|
||||||
> - `/chains/main/blocks/head/votes/proposals`
|
|
||||||
> - `/chains/main/blocks/head/votes/current_quorum`
|
|
||||||
|
|
||||||
[More about the Tezos `JSON/RPC` endpoints](https://tezos.gitlab.io/api/rpc.html)
|
|
||||||
|
|
||||||
## Nodes
|
|
||||||
|
|
||||||
Tezos has three types of nodes:
|
|
||||||
|
|
||||||
- Full mode (default mode)
|
|
||||||
- **Rolling mode**
|
|
||||||
- **Archive mode**
|
|
||||||
|
|
||||||
We use two types of mode:
|
|
||||||
|
|
||||||
- **Archive** to store the whole blockchain. Archive is the heaviest mode as it keeps the whole chain data to be able to query any information stored on the chain since the genesis. It is particularly suitable for indexers or block explorer, that is why we use archive nodes.
|
|
||||||
- **Rolling** to store last blocks (and scale them faster)
|
|
||||||
|
|
||||||
> [More about history modes](https://blog.nomadic-labs.com/introducing-snapshots-and-history-modes-for-the-tezos-node.html)
|
|
@ -1,6 +0,0 @@
|
|||||||
- [Usage](##usage)
|
|
||||||
- [Networks](#networks)
|
|
||||||
- [Make Requests](#make-requests)
|
|
||||||
- [Security](##security)
|
|
||||||
- [RPC Endpoints](#rpc-endpoints)
|
|
||||||
- [Nodes](#nodes)
|
|
@ -1,6 +0,0 @@
|
|||||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M21 18.5H7" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M21 14.5H3" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M21 10.5H7" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M21 6.5H3" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 518 B |
@ -1,3 +0,0 @@
|
|||||||
<svg width="10" height="8" viewBox="0 0 10 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M9.81353 0.144344C10.0429 0.354011 10.0635 0.715195 9.85967 0.95107L3.93375 7.80821C3.82832 7.9302 3.67721 8 3.51852 8C3.35983 8 3.20872 7.9302 3.10329 7.80821L0.140334 4.37964C-0.0635092 4.14376 -0.0428533 3.78258 0.18647 3.57291C0.415794 3.36324 0.766945 3.38449 0.970788 3.62037L3.51852 6.56846L9.02921 0.191798C9.23306 -0.0440776 9.58421 -0.0653237 9.81353 0.144344Z" fill="white"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 497 B |
@ -1,4 +0,0 @@
|
|||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M18 6L6 18" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M6 6L18 18" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 311 B |
@ -1,4 +0,0 @@
|
|||||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M18.8601 6.64001C20.1185 7.8988 20.9754 9.50246 21.3224 11.2482C21.6694 12.994 21.491 14.8034 20.8098 16.4478C20.1285 18.0921 18.9749 19.4976 17.4949 20.4864C16.015 21.4752 14.275 22.0029 12.4951 22.0029C10.7152 22.0029 8.97527 21.4752 7.49529 20.4864C6.01532 19.4976 4.86176 18.0921 4.18049 16.4478C3.49921 14.8034 3.32081 12.994 3.66784 11.2482C4.01487 9.50246 4.87174 7.8988 6.13012 6.64001" stroke="#999999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M12.5 2V12" stroke="#999999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 694 B |
@ -1,5 +0,0 @@
|
|||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<circle cx="6" cy="6" r="6" fill="#FFB017"/>
|
|
||||||
<path d="M6 3V5.66667" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<circle cx="6" cy="9" r="1" fill="white"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 295 B |
@ -1,5 +0,0 @@
|
|||||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M18 8.5C18 6.9087 17.3679 5.38258 16.2426 4.25736C15.1174 3.13214 13.5913 2.5 12 2.5C10.4087 2.5 8.88258 3.13214 7.75736 4.25736C6.63214 5.38258 6 6.9087 6 8.5C6 15.5 3 17.5 3 17.5H21C21 17.5 18 15.5 18 8.5Z" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M13.73 21.5C13.5542 21.8031 13.3019 22.0547 12.9982 22.2295C12.6946 22.4044 12.3504 22.4965 12 22.4965C11.6496 22.4965 11.3054 22.4044 11.0018 22.2295C10.6982 22.0547 10.4458 21.8031 10.27 21.5" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M8 11.1666H8.00667" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 801 B |
@ -1,12 +0,0 @@
|
|||||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g clip-path="url(#clip0_2395_56631)">
|
|
||||||
<path d="M10.9999 20.1667C16.0625 20.1667 20.1666 16.0627 20.1666 11C20.1666 5.93743 16.0625 1.83337 10.9999 1.83337C5.93731 1.83337 1.83325 5.93743 1.83325 11C1.83325 16.0627 5.93731 20.1667 10.9999 20.1667Z" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M11 14.6667V11" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M11 7.33337H11.0092" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_2395_56631">
|
|
||||||
<rect width="22" height="22" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 765 B |
@ -1,4 +0,0 @@
|
|||||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M20 21.5V19.5C20 18.4391 19.5786 17.4217 18.8284 16.6716C18.0783 15.9214 17.0609 15.5 16 15.5H8C6.93913 15.5 5.92172 15.9214 5.17157 16.6716C4.42143 17.4217 4 18.4391 4 19.5V21.5" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M12 11.5C14.2091 11.5 16 9.70914 16 7.5C16 5.29086 14.2091 3.5 12 3.5C9.79086 3.5 8 5.29086 8 7.5C8 9.70914 9.79086 11.5 12 11.5Z" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 598 B |
@ -1,38 +0,0 @@
|
|||||||
<svg width="174" height="39" viewBox="0 0 174 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g clip-path="url(#clip0_2800_16209)">
|
|
||||||
<path d="M25.5273 5.84731C25.5401 5.51025 25.3462 5.16586 24.929 4.98634L14.1634 0.368254C13.2979 -0.0036107 12.2788 -0.0036107 11.4116 0.368254L0.706259 4.96069C0.281779 5.14204 0.0823469 5.4901 0.0933248 5.83448V26.9282L2.49932 28.0621L3.93743 27.3733L10.7474 30.1852V31.6159L12.8332 32.5978L14.9391 31.5902V30.1999L21.7381 27.3916L23.1689 28.0621L25.5328 26.93V5.84731H25.5273Z" fill="#C5B2D4"/>
|
|
||||||
<path d="M12.8314 32.7L10.6541 31.676V30.249L3.93741 27.4756L2.49748 28.1644L0 26.9865L0.00365931 5.74256C0.0311041 5.3707 0.279937 5.04463 0.669653 4.8761L11.3768 0.283662C12.2605 -0.0955304 13.3162 -0.0955304 14.1999 0.283662L24.9656 4.90175C25.3517 5.06661 25.5932 5.38902 25.6188 5.75539H25.6206V26.9883L23.1671 28.1625L21.7344 27.4921L15.0306 30.2618V31.6485L12.8332 32.7H12.8314ZM10.837 31.5588L12.8314 32.4967L14.8458 31.5331V30.1391L14.9025 30.1153L21.7381 27.2924L23.1652 27.961L25.4377 26.8729V5.93857H25.434V5.84331C25.4486 5.50992 25.2456 5.22232 24.8924 5.07028L14.1267 0.452192C13.2888 0.0931497 12.2861 0.0931497 11.4481 0.452192L0.741009 5.04463C0.382398 5.1985 0.173817 5.4916 0.182965 5.83049V5.92392V26.8692L2.49748 27.9592L3.93192 27.2723L3.97035 27.2869L10.837 30.1226V31.5551V31.5588Z" fill="#4E1480"/>
|
|
||||||
<path d="M10.5187 28.7051C10.1985 28.7051 9.87462 28.641 9.56907 28.5109L2.57614 25.5305C1.68144 25.1495 1.10327 24.2739 1.10327 23.3011L1.09778 10.187C1.09778 9.36812 1.50579 8.61157 2.18825 8.16094C2.87071 7.7103 3.72699 7.6352 4.47898 7.96127L11.4774 10.9856C12.3648 11.3685 12.9393 12.2423 12.9393 13.2113V26.2816C12.9393 27.0967 12.5331 27.8533 11.8543 28.3039C11.4518 28.5714 10.9871 28.7069 10.5205 28.7069L10.5187 28.7051ZM9.78497 28.0053C10.365 28.2526 11.0255 28.1922 11.5506 27.8441C12.0757 27.4961 12.3904 26.9117 12.3904 26.2797V13.2095C12.3904 12.4603 11.9476 11.7862 11.2597 11.4894L4.26125 8.46502C3.68125 8.21406 3.01891 8.27268 2.49014 8.62073C1.96137 8.96878 1.64667 9.55497 1.64667 10.187L1.65216 23.3011C1.65216 24.054 2.0986 24.73 2.79021 25.0249L9.78314 28.0053H9.78497Z" fill="white"/>
|
|
||||||
<path d="M13.0509 15.5434V14.8564C13.0509 14.8381 13.0399 14.8235 13.0235 14.8161L12.1763 14.4498C12.1471 14.4369 12.1141 14.4589 12.1141 14.4901V15.177C12.1141 15.1953 12.1251 15.21 12.1416 15.2173L12.9887 15.5837C13.018 15.5965 13.0509 15.5745 13.0509 15.5434Z" fill="white"/>
|
|
||||||
<path d="M13.007 15.862C12.9631 15.862 12.921 15.8529 12.879 15.8345L12.0337 15.4682C11.9166 15.4187 11.8416 15.3033 11.8416 15.1751V14.4881C11.8416 14.38 11.8946 14.2811 11.9843 14.2207C12.0739 14.1621 12.1874 14.1529 12.2862 14.195L13.1333 14.5614C13.2504 14.6109 13.3254 14.7263 13.3254 14.8545V15.5414C13.3254 15.6495 13.2723 15.7484 13.1809 15.8089C13.1278 15.8437 13.0674 15.862 13.007 15.862ZM12.3904 15.0249L12.7765 15.1916V15.0084L12.3904 14.8417V15.0249Z" fill="white"/>
|
|
||||||
<path d="M13.0819 26.2944V25.6074C13.0819 25.5891 13.0709 25.5744 13.0545 25.5671L12.2074 25.2007C12.1781 25.1879 12.1451 25.2099 12.1451 25.241V25.928C12.1451 25.9463 12.1561 25.961 12.1726 25.9683L13.0197 26.3347C13.049 26.3475 13.0819 26.3255 13.0819 26.2944Z" fill="white"/>
|
|
||||||
<path d="M13.038 26.6149C12.996 26.6149 12.9539 26.6057 12.9118 26.5892L12.0647 26.2229C11.9476 26.1716 11.8726 26.0562 11.8726 25.9298V25.2428C11.8726 25.1348 11.9256 25.0358 12.0153 24.9754C12.1049 24.9149 12.2184 24.9076 12.3172 24.9497L13.1643 25.3161C13.2814 25.3656 13.3564 25.481 13.3564 25.6092V26.2961C13.3564 26.4042 13.3033 26.5031 13.2137 26.5618C13.1606 26.5966 13.1003 26.6149 13.038 26.6149ZM12.4215 25.7777L12.8075 25.9444V25.7594L12.4215 25.5927V25.7777Z" fill="white"/>
|
|
||||||
<path d="M4.94739 14.1675C3.52575 14.7207 3.18726 17.2596 4.18808 19.8352C5.1889 22.4126 7.15395 24.0521 8.57559 23.4971C9.99723 22.942 10.3357 20.4049 9.33489 17.8293C8.33407 15.2538 6.36903 13.6124 4.94739 14.1675ZM7.34606 18.8845C7.32959 18.824 7.30947 18.7636 7.28568 18.7031C7.23811 18.5786 7.17773 18.465 7.11187 18.3642L7.45218 15.818C7.99925 16.3529 8.50057 17.1167 8.8537 18.0235C9.19584 18.9028 9.34221 19.7766 9.31294 20.5258L7.34423 18.8826L7.34606 18.8845ZM5.31515 15.0706C5.79269 14.8837 6.34707 15.0065 6.89048 15.3618L6.5465 17.9228C6.48612 17.9154 6.42758 17.9209 6.37269 17.9411C6.33975 17.9539 6.30865 17.9722 6.2812 17.996L4.3436 16.3785C4.50095 15.7355 4.82846 15.2592 5.31332 15.0706H5.31515ZM4.70221 19.6447C4.35458 18.7489 4.20821 17.8605 4.2448 17.1021L6.10373 18.6537C6.11836 18.802 6.15496 18.9614 6.21717 19.1208C6.25559 19.2197 6.30133 19.3113 6.35073 19.3956L6.03237 21.7751C5.51275 21.2439 5.0407 20.5093 4.70404 19.6429L4.70221 19.6447ZM6.59224 22.2587L6.90877 19.8938C6.98562 19.9121 7.06064 19.9121 7.12833 19.8847C7.21616 19.8499 7.28385 19.7784 7.33142 19.6813L9.22146 21.2585C9.06776 21.918 8.7366 22.4089 8.24259 22.6013C7.74127 22.7955 7.15761 22.6544 6.59224 22.2587Z" fill="white"/>
|
|
||||||
<path d="M6.96733 18.639C7.22714 19.0585 7.22531 19.5293 6.9783 19.6795C6.7313 19.8297 6.31048 19.6099 6.07995 19.1739C5.83477 18.7104 5.82928 18.2928 6.05799 18.1462C6.32512 17.9777 6.7002 18.2104 6.96733 18.639Z" fill="#3FA79E"/>
|
|
||||||
<path d="M92.3262 11.9142C92.4908 11.2914 92.7653 10.7015 93.1495 10.1428C93.5337 9.58411 94.0003 9.095 94.5492 8.67185C95.0981 8.25052 95.7165 7.92079 96.4008 7.68265C97.0869 7.44451 97.8151 7.32544 98.5818 7.32544H102.589L102.04 9.60609H100.283C99.3685 9.60609 98.6092 9.8314 98.0054 10.2784C97.4016 10.7272 97.0266 11.2731 96.8802 11.9124L96.3038 14.193H99.7893L99.213 16.4737H95.7275L92.9007 27.9319H88.3448L92.3243 11.9124L92.3262 11.9142Z" fill="#4E1480"/>
|
|
||||||
<path d="M102.838 11.9142C103.002 11.2914 103.277 10.7015 103.661 10.1428C104.045 9.58411 104.512 9.095 105.061 8.67185C105.609 8.25052 106.228 7.92079 106.912 7.68265C107.598 7.44451 108.326 7.32544 109.093 7.32544H113.1L112.551 9.60609H110.795C109.88 9.60609 109.121 9.8314 108.517 10.2784C107.913 10.7272 107.538 11.2731 107.392 11.9124L106.815 14.193H110.301L109.724 16.4737H106.239L103.412 27.9319H98.8562L102.836 11.9124L102.838 11.9142Z" fill="#4E1480"/>
|
|
||||||
<path d="M122.872 11.9143L122.296 14.195H120.567C119.652 14.195 118.893 14.4203 118.289 14.8672C117.685 15.316 117.301 15.8619 117.136 16.5013L114.309 27.932H109.726L112.553 16.5287C112.699 15.9242 112.959 15.3435 113.336 14.783C113.711 14.2243 114.174 13.7297 114.723 13.2992C115.272 12.8687 115.894 12.5298 116.589 12.2825C117.284 12.0352 118.035 11.9106 118.84 11.9106H122.874L122.872 11.9143Z" fill="#4E1480"/>
|
|
||||||
<path d="M126.33 27.9338C124.976 27.9338 123.946 27.6041 123.242 26.9446C122.537 26.2851 122.184 25.4516 122.184 24.4441C122.184 24.1327 122.23 23.7663 122.322 23.345L124.023 16.4756C124.188 15.8528 124.462 15.2629 124.846 14.7042C125.231 14.1455 125.697 13.6601 126.246 13.2479C126.795 12.8357 127.417 12.5115 128.112 12.2715C128.808 12.0334 129.54 11.9143 130.308 11.9143H133.766C135.12 11.9143 136.145 12.2404 136.84 12.8907C137.535 13.541 137.883 14.3708 137.883 15.3783C137.883 15.7081 137.837 16.0836 137.746 16.5049L136.62 21.0662H127.481L126.905 23.3743C126.74 24.0155 126.844 24.5559 127.221 24.9955C127.596 25.4352 128.24 25.655 129.157 25.655H133.768L133.219 27.9356H126.33V27.9338ZM128.059 18.7837H132.643L133.192 16.4903C133.338 15.8528 133.246 15.3105 132.917 14.8654C132.588 14.4184 132.103 14.1968 131.463 14.1968C131.151 14.1968 130.84 14.2554 130.529 14.3745C130.218 14.4935 129.935 14.6566 129.679 14.8654C129.422 15.0742 129.199 15.3197 129.007 15.6018C128.815 15.8839 128.681 16.1807 128.61 16.4903L128.061 18.7837H128.059Z" fill="#4E1480"/>
|
|
||||||
<path d="M140.684 27.9337V23.345H145.267V27.9337H140.684Z" fill="#4E1480"/>
|
|
||||||
<path d="M152.099 11.9142H156.682L152.703 27.9337H148.12L152.099 11.9142ZM153.224 7.32544H157.808L157.259 9.60609H152.675L153.224 7.32544Z" fill="#3FA79E"/>
|
|
||||||
<path d="M162.446 27.9338C161.092 27.9338 160.062 27.6041 159.357 26.9446C158.653 26.2851 158.3 25.4516 158.3 24.4441C158.3 24.1327 158.346 23.7664 158.437 23.345L160.139 16.4756C160.303 15.8528 160.578 15.2629 160.962 14.7042C161.346 14.1455 161.813 13.6601 162.362 13.2479C162.911 12.8357 163.533 12.5115 164.228 12.2715C164.923 12.0334 165.655 11.9143 166.423 11.9143H169.882C171.235 11.9143 172.26 12.244 172.955 12.9035C173.651 13.563 173.998 14.3873 173.998 15.3765C173.998 15.6879 173.952 16.0543 173.861 16.4756L172.159 23.345C171.995 23.9862 171.72 24.5815 171.336 25.1311C170.952 25.6806 170.485 26.1661 169.936 26.5874C169.387 27.0087 168.769 27.3384 168.085 27.5766C167.399 27.8147 166.67 27.9338 165.902 27.9338H162.444H162.446ZM169.28 16.5031C169.444 15.8619 169.362 15.3179 169.033 14.8691C168.703 14.4203 168.218 14.1968 167.578 14.1968C166.938 14.1968 166.343 14.4221 165.794 14.8691C165.245 15.3179 164.888 15.8638 164.724 16.5031L163.022 23.345C162.857 23.9862 162.94 24.5321 163.269 24.979C163.598 25.4278 164.083 25.6513 164.724 25.6513C165.035 25.6513 165.346 25.5927 165.657 25.4736C165.968 25.3546 166.251 25.1897 166.508 24.979C166.764 24.7684 166.983 24.5266 167.166 24.2518C167.349 23.977 167.486 23.6748 167.578 23.345L169.28 16.5031Z" fill="#3FA79E"/>
|
|
||||||
<path d="M83.5749 13.4C80.0821 12.5042 76.4393 14.9497 75.4385 18.8607C74.4376 22.7717 76.4576 26.6699 79.9522 27.5657C83.445 28.4614 87.0879 26.0159 88.0887 22.1049C89.0895 18.1939 87.0696 14.2958 83.5749 13.4ZM82.876 21.1597C82.9126 21.0754 82.9437 20.9875 82.9675 20.8959C83.015 20.7072 83.0297 20.5185 83.0132 20.3372L86.667 17.8844C87.1391 19.0567 87.2507 20.4325 86.8994 21.81C86.5573 23.1454 85.8345 24.2665 84.8996 25.0652L82.8742 21.1597H82.876ZM83.2584 14.7867C84.433 15.0871 85.4009 15.8107 86.0724 16.7797L82.3966 19.2472C82.285 19.174 82.1624 19.119 82.027 19.0842C81.9447 19.0622 81.8624 19.0512 81.78 19.0476L79.7875 15.2025C80.8524 14.6585 82.0655 14.4808 83.2584 14.7867ZM76.6918 19.1941C77.0394 17.8349 77.7822 16.6973 78.741 15.8968L80.653 19.5843C80.5084 19.762 80.3968 19.9782 80.3346 20.22C80.2962 20.3702 80.2797 20.5204 80.2815 20.6669L76.8638 22.9604C76.4429 21.8228 76.3551 20.5094 76.6918 19.1941ZM77.4273 24.0888L80.8231 21.81C80.9549 21.9126 81.1067 21.9914 81.2751 22.0335C81.4928 22.0884 81.7123 22.0811 81.9191 22.0243L83.8622 25.7741C82.7845 26.342 81.5477 26.5307 80.3346 26.2193C79.1051 25.9042 78.1006 25.1275 77.4273 24.0888Z" fill="#3FA79E"/>
|
|
||||||
<path d="M81.4104 27.8405C80.9183 27.8405 80.4224 27.78 79.9303 27.6536C78.2159 27.214 76.7942 26.0526 75.9306 24.3856C75.0689 22.7223 74.8621 20.7512 75.3525 18.837C75.841 16.9227 76.9681 15.2942 78.5233 14.25C80.0821 13.204 81.8843 12.8706 83.6005 13.3103C87.1373 14.217 89.1901 18.172 88.1802 22.127C87.3093 25.5305 84.4477 27.8405 81.4123 27.8405H81.4104ZM82.1075 13.3048C80.9055 13.3048 79.7052 13.6785 78.6257 14.4039C77.1071 15.4224 76.0075 17.0143 75.5299 18.8846C75.0524 20.7549 75.2518 22.6783 76.0935 24.3032C76.9333 25.9244 78.3128 27.051 79.976 27.4778C83.4139 28.3607 87.0147 25.939 88.0009 22.083C88.987 18.227 86.9927 14.3709 83.553 13.4898C83.0754 13.3671 82.5906 13.3066 82.1057 13.3066L82.1075 13.3048ZM81.5074 26.4574C81.1049 26.4574 80.7042 26.408 80.3127 26.3072C79.1106 25.9995 78.0585 25.2283 77.3504 24.1383L77.301 24.0632L80.8268 21.6965L80.8798 21.7368C81.0079 21.8357 81.147 21.9053 81.2988 21.9438C81.4946 21.9932 81.694 21.9914 81.8953 21.9346L81.9667 21.9145L83.9866 25.8108L83.9061 25.8529C83.1468 26.2523 82.3253 26.4556 81.5074 26.4556V26.4574ZM77.5535 24.1145C78.236 25.1257 79.2277 25.8401 80.3584 26.1314C81.4855 26.4208 82.6528 26.2834 83.7396 25.7357L81.8715 22.1324C81.6629 22.1782 81.4562 22.1764 81.2531 22.1251C81.0994 22.0867 80.9549 22.0189 80.8213 21.9236L77.5554 24.1163L77.5535 24.1145ZM84.874 25.2099L82.7754 21.1634L82.7937 21.1231C82.8303 21.0388 82.8595 20.9564 82.8815 20.874C82.9254 20.7018 82.94 20.5241 82.9254 20.3464L82.9199 20.2933L86.7128 17.747L86.7549 17.8514C87.2489 19.0806 87.333 20.4948 86.9909 21.8339C86.6542 23.1473 85.9535 24.2904 84.9618 25.1348L84.874 25.2099ZM82.9785 21.1561L84.9307 24.9205C85.8474 24.1072 86.4969 23.0264 86.8134 21.7881C87.1373 20.5241 87.0696 19.1923 86.6268 18.0236L83.1102 20.383C83.1193 20.5644 83.1029 20.7439 83.0571 20.9179C83.037 20.9967 83.0114 21.0755 82.9785 21.1542V21.1561ZM76.818 23.1015L76.7778 22.9934C76.3332 21.7881 76.2728 20.4673 76.6021 19.1722C76.9388 17.8533 77.678 16.6644 78.6806 15.8272L78.7684 15.754L80.7609 19.5953L80.7225 19.6411C80.5798 19.817 80.4792 20.0185 80.4224 20.242C80.3877 20.3812 80.3694 20.5223 80.373 20.6633V20.7128L76.8198 23.0997L76.818 23.1015ZM76.6918 19.1942L76.7796 19.2161C76.4686 20.4362 76.5125 21.6781 76.9095 22.8194L80.1882 20.6193C80.1882 20.4783 80.2084 20.3372 80.2431 20.1998C80.3017 19.9709 80.4023 19.762 80.5414 19.577L78.7099 16.0434C77.7804 16.8512 77.0961 17.9742 76.7778 19.218L76.6899 19.196L76.6918 19.1942ZM82.3985 19.3572L82.3472 19.3242C82.2411 19.2546 82.1277 19.2033 82.0051 19.1722C81.9337 19.1539 81.8587 19.1429 81.7764 19.1392H81.7233L79.6631 15.1641L79.7455 15.122C80.8798 14.5431 82.1021 14.3966 83.2804 14.6988C84.4422 14.9956 85.432 15.699 86.1474 16.7285L86.2005 16.8054L82.3966 19.359L82.3985 19.3572ZM81.8386 18.9597C81.9136 18.9652 81.9831 18.978 82.0508 18.9945C82.1716 19.0256 82.2868 19.0733 82.3948 19.1374L85.9425 16.756C85.2582 15.8034 84.3233 15.1549 83.2346 14.8747C82.1277 14.5907 80.9805 14.719 79.9101 15.2429L81.8368 18.9597H81.8386Z" fill="#3FA79E"/>
|
|
||||||
<path d="M34.8329 7.37695H39.4162L34.8603 25.7046H41.7215L41.1726 27.9852H29.7281L34.8329 7.37695Z" fill="#4E1480"/>
|
|
||||||
<path d="M49.1572 7.37695H60.6017L60.0528 9.6576H53.1916L51.4626 16.5545H57.72L57.1711 18.8351H50.9137L49.1847 25.7046H56.0459L55.497 27.9852H44.0525L49.1572 7.37695Z" fill="#4E1480"/>
|
|
||||||
<path d="M65.7887 23.3964C65.6241 24.0376 65.7064 24.5835 66.0357 25.0304C66.3651 25.4792 66.8499 25.7027 67.4903 25.7027C68.826 25.7027 69.7865 24.9333 70.372 23.3946H72.6499C72.1742 24.9333 71.4424 26.0837 70.4544 26.844C69.4663 27.6042 68.1124 27.9834 66.3925 27.9834H65.2124C63.8585 27.9834 62.8284 27.6536 62.1239 26.9942C61.4195 26.3347 61.0664 25.5012 61.0664 24.4937C61.0664 24.1823 61.1121 23.8159 61.2036 23.3946L62.9052 16.5252C63.0699 15.9023 63.3443 15.3125 63.7285 14.7538C64.1128 14.1951 64.5793 13.7096 65.1282 13.2975C65.6771 12.8853 66.2992 12.561 66.9945 12.3211C67.6897 12.0829 68.4216 11.9639 69.1901 11.9639H70.3702C71.8522 11.9639 72.9134 12.2258 73.5538 12.7479C74.1942 13.27 74.5144 14.0522 74.5144 15.0982C74.5144 15.5378 74.4595 16.0141 74.3497 16.527H72.0718C72.273 15.0066 71.6967 14.2463 70.3427 14.2463C69.7024 14.2463 69.1077 14.4717 68.5588 14.9186C68.0099 15.3674 67.6532 15.9133 67.4885 16.5526L65.7869 23.3946L65.7887 23.3964Z" fill="#4E1480"/>
|
|
||||||
<path d="M134.897 37.6811C134.613 37.6811 134.251 37.5895 134.081 37.3844V37.5749H133.599V32.6948H134.093V34.6128C134.331 34.2574 134.666 33.9863 135.134 33.9863C135.925 33.9863 136.424 34.7502 136.424 35.7595C136.424 36.7689 135.905 37.6774 134.897 37.6774V37.6811ZM134.97 34.4571C134.62 34.4571 134.397 34.6476 134.245 34.8454C134.121 35.0103 134.093 35.0836 134.093 35.2612V36.778C134.093 37.0619 134.344 37.2799 134.818 37.2799C135.542 37.2799 135.91 36.6461 135.91 35.8694C135.91 35.0927 135.601 34.4589 134.968 34.4589L134.97 34.4571Z" fill="#320756"/>
|
|
||||||
<path d="M138.481 37.8203C138.283 38.4065 137.954 38.9341 137.085 39L137.045 38.5586C137.584 38.5512 137.921 38.2087 138.066 37.6884L136.794 34.0889H137.334L138.033 36.212C138.164 36.6077 138.276 36.9832 138.316 37.1096H138.329C138.375 36.9246 138.481 36.582 138.6 36.1937L139.246 34.0907H139.714L138.483 37.8222L138.481 37.8203Z" fill="#320756"/>
|
|
||||||
<path d="M144.455 37.5766V33.6637C144.455 32.8504 143.983 32.3778 143.137 32.3778C142.599 32.3778 142.127 32.6196 141.798 33.0482C141.589 33.301 141.556 33.4219 141.556 33.7517V37.5766H140.733V31.7623H141.502V32.6855C141.941 32.0371 142.555 31.5974 143.401 31.5974C144.51 31.5974 145.278 32.2789 145.278 33.5318V37.5766H144.455Z" fill="#059C8C"/>
|
|
||||||
<path d="M149.219 37.7854C147.408 37.7854 146.65 36.2247 146.65 34.6529C146.65 33.0812 147.485 31.5645 149.263 31.5645C151.041 31.5645 151.832 33.0812 151.832 34.6639C151.832 36.2467 150.998 37.7854 149.219 37.7854ZM149.263 32.2789C147.957 32.2789 147.507 33.5868 147.507 34.6529C147.507 35.7191 147.891 37.06 149.219 37.06C150.547 37.06 150.976 35.6971 150.976 34.6639C150.976 33.6308 150.591 32.2789 149.263 32.2789Z" fill="#059C8C"/>
|
|
||||||
<path d="M154.873 37.7635C153.841 37.7635 153.391 37.0491 153.391 36.1808V32.5647H152.414V31.8723H153.391V30.5973L154.214 30.3005V31.8723H155.795V32.5647H154.214V36.0818C154.214 36.7193 154.532 37.0491 155.136 37.0491C155.389 37.0491 155.608 36.9721 155.773 36.8842L155.927 37.5656C155.663 37.6755 155.257 37.7635 154.873 37.7635Z" fill="#059C8C"/>
|
|
||||||
<path d="M157.661 37.6426C157.321 37.6426 157.09 37.4008 157.09 37.0601C157.09 36.7193 157.31 36.4775 157.65 36.4775C157.991 36.4775 158.221 36.7193 158.221 37.0601C158.221 37.4008 158.001 37.6426 157.661 37.6426Z" fill="#25124B"/>
|
|
||||||
<path d="M159.418 37.5766V29.8169H160.318V37.5766H159.418Z" fill="#25124B"/>
|
|
||||||
<path d="M164.127 30.6412V37.5766H163.216V30.6412H160.658V29.8169H166.718V30.6412H164.127Z" fill="#25124B"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_2800_16209">
|
|
||||||
<rect width="174" height="39" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 17 KiB |
@ -1,101 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
border: 1px solid;
|
|
||||||
gap: 12px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: fit-content;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
background: transparent;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
user-select: none;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[variant="primary"] {
|
|
||||||
color: $white;
|
|
||||||
background-color: $purple-flash;
|
|
||||||
border-color: $purple-flash;
|
|
||||||
padding: 24px 48px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 22px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: $purple-hover;
|
|
||||||
background-color: $purple-hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
border-color: $purple-soft;
|
|
||||||
background-color: $purple-soft;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[variant="secondary"] {
|
|
||||||
color: $white;
|
|
||||||
background-color: $red-flash;
|
|
||||||
border-color: $red-flash;
|
|
||||||
padding: 24px 48px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 22px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: $re-hover;
|
|
||||||
background-color: $re-hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
border-color: $red-soft;
|
|
||||||
background-color: $red-soft;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[variant="ghost"] {
|
|
||||||
color: $pink-flash;
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: $pink-flash;
|
|
||||||
padding: 24px 48px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 22px;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
path {
|
|
||||||
stroke: $white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: $pink-hover;
|
|
||||||
color: $pink-hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
border-color: $pink-soft;
|
|
||||||
background-color: $pink-soft;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[fullwidth="true"] {
|
|
||||||
width: 100%;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[touppercase="false"] {
|
|
||||||
text-transform: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
export enum EButtonVariant {
|
|
||||||
PRIMARY = "primary",
|
|
||||||
SECONDARY = "secondary",
|
|
||||||
GHOST = "ghost",
|
|
||||||
LINE = "line",
|
|
||||||
}
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
variant?: EButtonVariant;
|
|
||||||
fullwidth?: "true" | "false";
|
|
||||||
icon?: React.ReactNode;
|
|
||||||
disabled?: boolean;
|
|
||||||
type: "button" | "submit";
|
|
||||||
isloading: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type IState = {};
|
|
||||||
|
|
||||||
export default class Button extends React.Component<IProps, IState> {
|
|
||||||
static defaultProps: IProps = {
|
|
||||||
variant: EButtonVariant.PRIMARY,
|
|
||||||
disabled: false,
|
|
||||||
type: "button",
|
|
||||||
isloading: "false",
|
|
||||||
fullwidth: "false",
|
|
||||||
};
|
|
||||||
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
const attributes = { ...this.props };
|
|
||||||
delete attributes.icon;
|
|
||||||
// let icon = this.props.isloading === "true" ? <Loader /> : this.props.icon; // Notion de loader
|
|
||||||
let icon = this.props.icon;
|
|
||||||
return (
|
|
||||||
<button {...attributes} onClick={this.props.onClick} className={classes["root"]} type={this.props.type}>
|
|
||||||
{this.props.children}
|
|
||||||
{this.props.icon && icon}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
appearance: none;
|
|
||||||
background-color: transparent;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border: 1px solid var(green-flash);
|
|
||||||
border-radius: 2px;
|
|
||||||
margin-right: 16px;
|
|
||||||
display: grid;
|
|
||||||
place-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]::before {
|
|
||||||
content: url("../../../Assets/icons/check.svg");
|
|
||||||
place-content: center;
|
|
||||||
display: grid;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
background-color: var(green-flash);
|
|
||||||
border-radius: 2px;
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:checked::before {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Tooltip from "../ToolTip";
|
|
||||||
import Typography, { ITypo, ITypoColor } from "../Typography";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
name: string;
|
|
||||||
toolTip?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class CheckBox extends React.Component<IProps> {
|
|
||||||
static defaultProps = {
|
|
||||||
toolTip: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Typography typo={ITypo.P_ERR_16} color={ITypoColor.BLACK}>
|
|
||||||
<label className={classes["root"]}>
|
|
||||||
<input type="checkbox" name={this.props.name} />
|
|
||||||
{this.props.name}
|
|
||||||
{this.props.toolTip && <Tooltip className={classes["tooltip"]} text={this.props.toolTip} />}
|
|
||||||
</label>
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
import { ChangeEvent, Component, createRef } from "react";
|
|
||||||
|
|
||||||
import { FormContext, IFormContext } from "..";
|
|
||||||
// elements
|
|
||||||
import Validators, { IValidationTypes } from "../Validators/Validators";
|
|
||||||
|
|
||||||
export type IError = {
|
|
||||||
message: string;
|
|
||||||
validator: string;
|
|
||||||
value: string | number | readonly string[];
|
|
||||||
args: any[];
|
|
||||||
isErrored?: (hasError: boolean) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type INewBasefieldProps = {
|
|
||||||
onChange?: (event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) => void;
|
|
||||||
name: string;
|
|
||||||
regex?: RegExp;
|
|
||||||
onCancel?: () => void;
|
|
||||||
disableValidation?: boolean;
|
|
||||||
onErrors?: (errors: IError[]) => void;
|
|
||||||
fieldRef?: React.RefObject<any>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type IProps = IValidationTypes & React.InputHTMLAttributes<HTMLInputElement> & INewBasefieldProps;
|
|
||||||
|
|
||||||
type IState = {
|
|
||||||
value?: string | number | readonly string[];
|
|
||||||
errors: IError[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export default abstract class BaseField<P extends IProps> extends Component<P, IState> {
|
|
||||||
public static override contextType = FormContext;
|
|
||||||
public override context: IFormContext | null = null;
|
|
||||||
public fieldRef: React.RefObject<any> = createRef();
|
|
||||||
|
|
||||||
static defaultProps: Partial<IProps> = {
|
|
||||||
disableValidation: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: P) {
|
|
||||||
super(props);
|
|
||||||
this.onChange = this.onChange.bind(this);
|
|
||||||
this.validate = this.validate.bind(this);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
value: this.props.value ?? this.props.defaultValue ?? "",
|
|
||||||
errors: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentDidMount() {
|
|
||||||
this.context?.setField(this.props.name, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentDidUpdate(prevProps: P) {
|
|
||||||
if (prevProps.value !== this.props.value || prevProps.defaultValue !== this.props.defaultValue) {
|
|
||||||
this.setState({ value: this.props.value ?? this.props.defaultValue ?? "" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentWillUnmount() {
|
|
||||||
this.context?.unSetField(this.props.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onBlur(event: React.FocusEvent<HTMLInputElement, Element>) {
|
|
||||||
// this.validate();
|
|
||||||
// if (this.props.onBlur) {
|
|
||||||
// this.props.onBlur(event);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public async validate(isOnSubmit?: boolean) {
|
|
||||||
if (this.props.disableValidation) return;
|
|
||||||
if (this.props.readOnly) return;
|
|
||||||
|
|
||||||
const errorArray: IError[] = [];
|
|
||||||
const props: { [key: string]: any } = this.props;
|
|
||||||
const validators = Object.entries(Validators).filter(([key]) => props[key]);
|
|
||||||
|
|
||||||
const isValidable = isOnSubmit
|
|
||||||
? this.props.required || (this.state.value && this.state.value !== "")
|
|
||||||
: this.state.value && this.state.value !== "";
|
|
||||||
|
|
||||||
if (isValidable) {
|
|
||||||
const validations = await Promise.all(
|
|
||||||
validators.map(async ([key, validator]) => {
|
|
||||||
const validation = await (validator.validate as any)(this.state.value, ...(props[key].args ?? []));
|
|
||||||
if (props[key].isErrored) {
|
|
||||||
props[key].isErrored(!validation);
|
|
||||||
}
|
|
||||||
return [key, validator, validation];
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const unValidateds = validations.filter(([key, validator, validation]) => !validation);
|
|
||||||
const errors: IError[] = unValidateds.map(([key, unValidated]) => {
|
|
||||||
let message = unValidated.message;
|
|
||||||
if (typeof props[key] === "object" && props[key].message) message = props[key].message;
|
|
||||||
return { message, validator: key, value: this.state.value!, args: props[key].args ?? [] };
|
|
||||||
});
|
|
||||||
|
|
||||||
errorArray.push(...errors);
|
|
||||||
} else {
|
|
||||||
validators.forEach(async ([key]) => {
|
|
||||||
if (props[key].isErrored) {
|
|
||||||
props[key].isErrored(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ errors: errorArray });
|
|
||||||
this.onErrors(errorArray);
|
|
||||||
return errorArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setErrors(errors: IError[]) {
|
|
||||||
this.setState({ ...this.state, errors });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* It is automatically called by the parent form when the user cancelled the
|
|
||||||
* form and all of its changes.
|
|
||||||
*
|
|
||||||
* Override the method for custom cancelling logic, or pass a custom onCancel
|
|
||||||
* callback.
|
|
||||||
*/
|
|
||||||
public cancel() {
|
|
||||||
if (this.props.onCancel) {
|
|
||||||
this.props.onCancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public onErrors(errors: IError[]) {
|
|
||||||
if (this.props.onErrors) {
|
|
||||||
this.props.onErrors(errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected onChange(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) {
|
|
||||||
if (this.props.regex) {
|
|
||||||
if (!this.props.regex.test(event.currentTarget.value)) {
|
|
||||||
event.currentTarget.value = event.currentTarget.value.substring(0, event.currentTarget.value.length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setState({ value: event.currentTarget.value }, () => {
|
|
||||||
this.validate();
|
|
||||||
this.context?.onFieldChange(this.props.name, this);
|
|
||||||
});
|
|
||||||
if (this.props.onChange) {
|
|
||||||
this.props.onChange(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
position: relative;
|
|
||||||
textarea{
|
|
||||||
resize: none;
|
|
||||||
height: auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: 'Inter';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
.input {
|
|
||||||
z-index: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24px;
|
|
||||||
gap: 10px;
|
|
||||||
width: 530px;
|
|
||||||
height: 70px;
|
|
||||||
border: 1px solid $grey-medium;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
~ .fake-placeholder {
|
|
||||||
transform: translateY(-35px);
|
|
||||||
transition: transform 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:not([value=""]) {
|
|
||||||
~ .fake-placeholder {
|
|
||||||
transform: translateY(-35px);
|
|
||||||
transition: transform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[type="number"] {
|
|
||||||
&:focus {
|
|
||||||
~ .fake-placeholder {
|
|
||||||
transform: translateY(-35px);
|
|
||||||
transition: transform 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:not([value=""]) {
|
|
||||||
~ .fake-placeholder {
|
|
||||||
transform: translateY(-35px);
|
|
||||||
transition: transform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not([value=""]) {
|
|
||||||
~ .fake-placeholder {
|
|
||||||
transform: translateY(-35px);
|
|
||||||
transition: transform 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~ .fake-placeholder {
|
|
||||||
z-index: 2;
|
|
||||||
top: 35%;
|
|
||||||
margin-left: 8px;
|
|
||||||
padding: 0 16px;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
background: $white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.textarea {
|
|
||||||
z-index: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24px;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
width: 530px;
|
|
||||||
height: 70px;
|
|
||||||
border: 1px solid $grey-medium;
|
|
||||||
|
|
||||||
~ .fake-placeholder {
|
|
||||||
z-index: 2;
|
|
||||||
top: -12px;
|
|
||||||
margin-left: 8px;
|
|
||||||
padding: 0 16px;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
background: $white;
|
|
||||||
// transform: translateY(-35px);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
|
|
||||||
import { ReactNode } from "react";
|
|
||||||
|
|
||||||
import Validators from "../../Validators/Validators";
|
|
||||||
import BaseField, { IProps as IBaseFieldProps } from "../BaseField";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
export type IProps = IBaseFieldProps & {
|
|
||||||
fakeplaceholder: string;
|
|
||||||
textarea?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-ignore TODO: typing error on IProps (validator class?? cf Massi 22/02/23)
|
|
||||||
export default class InputField extends BaseField<IProps> {
|
|
||||||
public override render(): ReactNode {
|
|
||||||
let pattern;
|
|
||||||
|
|
||||||
if (this.props.type === "number") {
|
|
||||||
pattern = "(^[0-9]*)(\\.{0,1})([0-9]*)$";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.pattern) {
|
|
||||||
pattern = this.props.pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.fieldRef) {
|
|
||||||
this.fieldRef = this.props.fieldRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we always need to control the input so we need to set the value as "" by default
|
|
||||||
const value = this.state.value ?? "";
|
|
||||||
|
|
||||||
if (this.props.textarea === true) {
|
|
||||||
return (
|
|
||||||
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<textarea
|
|
||||||
maxLength={this.props.maxLength}
|
|
||||||
name={this.props.name}
|
|
||||||
required={this.props.required}
|
|
||||||
ref={this.props.fieldRef}
|
|
||||||
rows={4}
|
|
||||||
value={value}
|
|
||||||
onChange={this.onChange}
|
|
||||||
data-has-validation-errors={this.state.errors.length > 0}
|
|
||||||
className={
|
|
||||||
this.props.className ? [classes["textarea"], classes[this.props.className]].join(" ") : classes["textarea"]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div className={classes["fake-placeholder"]}>{this.props.fakeplaceholder}</div>
|
|
||||||
</div>
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<input
|
|
||||||
{...this.getHtmlAttributes()}
|
|
||||||
ref={this.props.fieldRef}
|
|
||||||
pattern={pattern}
|
|
||||||
onChange={this.onChange}
|
|
||||||
onBlur={this.onBlur}
|
|
||||||
value={value}
|
|
||||||
data-has-validation-errors={this.state.errors.length > 0}
|
|
||||||
className={
|
|
||||||
this.props.className ? [classes["input"], classes[this.props.className]].join(" ") : classes["input"]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div className={classes["fake-placeholder"]}>{this.props.fakeplaceholder}</div>
|
|
||||||
</div>
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We filter the props we'll pass to the primitive input as they're useless for it
|
|
||||||
// It also avoids the console warning because of passing useless props to a primitive DOM element
|
|
||||||
private getHtmlAttributes() {
|
|
||||||
const htmlAttributes = { ...this.props };
|
|
||||||
|
|
||||||
delete htmlAttributes.disableValidation;
|
|
||||||
delete htmlAttributes.onErrors;
|
|
||||||
delete htmlAttributes.fieldRef;
|
|
||||||
delete htmlAttributes.className;
|
|
||||||
delete htmlAttributes.defaultValue;
|
|
||||||
|
|
||||||
for (const validator in Validators) {
|
|
||||||
delete (htmlAttributes as { [key: string]: any })[validator];
|
|
||||||
}
|
|
||||||
|
|
||||||
return htmlAttributes;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
import { ReactNode } from "react";
|
|
||||||
|
|
||||||
import Validators from "../Validators/Validators";
|
|
||||||
//import { IProps as IBaseFieldProps } from "../.";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import BaseField, { IProps as IBaseFieldProps } from "../Elements/BaseField";
|
|
||||||
|
|
||||||
export type IProps = IBaseFieldProps & {
|
|
||||||
large?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class InputField extends BaseField<IProps> {
|
|
||||||
public override render(): ReactNode {
|
|
||||||
let pattern;
|
|
||||||
|
|
||||||
if (this.props.type === "number") {
|
|
||||||
pattern = "(^[0-9]*)(\\.{0,1})([0-9]*)$";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.pattern) {
|
|
||||||
pattern = this.props.pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.fieldRef) {
|
|
||||||
this.fieldRef = this.props.fieldRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we always need to control the input so we need to set the value as "" by default
|
|
||||||
const value = this.state.value ?? "";
|
|
||||||
|
|
||||||
if (this.props.large === true) {
|
|
||||||
return (
|
|
||||||
<textarea
|
|
||||||
maxLength={this.props.maxLength}
|
|
||||||
name={this.props.name}
|
|
||||||
required={this.props.required}
|
|
||||||
ref={this.props.fieldRef}
|
|
||||||
rows={4}
|
|
||||||
value={value}
|
|
||||||
onChange={this.onChange}
|
|
||||||
data-has-validation-errors={this.state.errors.length > 0}
|
|
||||||
className={this.props.className ? [classes["textarea"], classes[this.props.className]].join(" ") : classes["textarea"]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
{...this.getHtmlAttributes()}
|
|
||||||
ref={this.props.fieldRef}
|
|
||||||
pattern={pattern}
|
|
||||||
onChange={this.onChange}
|
|
||||||
value={value}
|
|
||||||
data-has-validation-errors={this.state.errors.length > 0}
|
|
||||||
className={this.props.className ? [classes["input"], classes[this.props.className]].join(" ") : classes["input"]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We filter the props we'll pass to the primitive input as they're useless for it
|
|
||||||
// It also avoids the console warning because of passing useless props to a primitive DOM element
|
|
||||||
private getHtmlAttributes() {
|
|
||||||
const htmlAttributes = { ...this.props };
|
|
||||||
|
|
||||||
delete htmlAttributes.disableValidation;
|
|
||||||
delete htmlAttributes.onErrors;
|
|
||||||
delete htmlAttributes.fieldRef;
|
|
||||||
delete htmlAttributes.className;
|
|
||||||
delete htmlAttributes.defaultValue;
|
|
||||||
|
|
||||||
for (const validator in Validators) {
|
|
||||||
delete (htmlAttributes as { [key: string]: any })[validator];
|
|
||||||
}
|
|
||||||
|
|
||||||
return htmlAttributes;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +0,0 @@
|
|||||||
import { isEmail, isNotEmpty, isNumberString, isString, max, maxLength, min, minLength } from "class-validator";
|
|
||||||
|
|
||||||
const Validators = {
|
|
||||||
/**
|
|
||||||
* **Parameters** : boolean
|
|
||||||
*
|
|
||||||
* This validator verifies the value is not empty
|
|
||||||
*/
|
|
||||||
required: { validate: isNotEmpty, message: "validation_messages.field_required" },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* **Parameters** : boolean
|
|
||||||
*
|
|
||||||
* This validator verifies the value is a number
|
|
||||||
*/
|
|
||||||
numbersOnly: { validate: isNumberString, message: "validation_messages.only_numbers" },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* **Parameters** : boolean
|
|
||||||
*
|
|
||||||
* This validator verifies the value is a number
|
|
||||||
*/
|
|
||||||
intOnly: {
|
|
||||||
validate: function (value: string) {
|
|
||||||
const regex = /^[0-9]*$/;
|
|
||||||
return regex.test(value);
|
|
||||||
},
|
|
||||||
message: "validation_messages.only_integers",
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* **Parameters** : number
|
|
||||||
*
|
|
||||||
* This validator verifies the number is not below the parameter
|
|
||||||
*/
|
|
||||||
minNumber: { validate: (value: string, minVal: number) => min(Number(value), minVal), message: "validation_messages.below_min" },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* **Parameters** : number
|
|
||||||
*
|
|
||||||
* This validator verifies the number is not above the parameter
|
|
||||||
*/
|
|
||||||
maxNumber: { validate: (value: string, maxVal: number) => max(Number(value), maxVal), message: "validation_messages.above_max" },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* **Parameters** : number
|
|
||||||
*
|
|
||||||
* This validator verifies the string minimum length is conform to the parameter
|
|
||||||
*/
|
|
||||||
minLength: {
|
|
||||||
validate: minLength,
|
|
||||||
message: "validation_messages.min_string_length",
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* **Parameters** : number
|
|
||||||
*
|
|
||||||
* This validator verifies the string maximum length is conform to the parameter
|
|
||||||
*/
|
|
||||||
maxLength: {
|
|
||||||
validate: maxLength,
|
|
||||||
message: "validation_messages.max_string_length",
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* **Parameters** : boolean
|
|
||||||
*
|
|
||||||
* This validator verifies the input's value is a string.
|
|
||||||
*/
|
|
||||||
isString: { validate: (value: string) => isString(value), message: "validation_messages.only_letters" },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* **Parameters** : boolean
|
|
||||||
*
|
|
||||||
* This validator verifies the input's value is conform to the tag regex.
|
|
||||||
*/
|
|
||||||
isTag: {
|
|
||||||
validate: function (value: string) {
|
|
||||||
const regex = /^[a-zA-Z0-9][a-zA-Z0-9 ]*(,[a-zA-Z0-9][a-zA-Z0-9 ]*)*$/;
|
|
||||||
const isValid = regex.test(value);
|
|
||||||
if (!isValid) return false;
|
|
||||||
|
|
||||||
const splittedTag = value.split(",");
|
|
||||||
if (splittedTag.length !== new Set(splittedTag).size) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
message: "validation_messages.not_valid_tag",
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* **Parameters** : boolean
|
|
||||||
*
|
|
||||||
* This validator verifies the input's value is a valid email.
|
|
||||||
*
|
|
||||||
* If the **input is empty, it is considered valid**. If you do not wish this
|
|
||||||
* to happen please refer to the `required` validator.
|
|
||||||
*/
|
|
||||||
isEmail: {
|
|
||||||
validate: (value: string) => (Boolean(value) ? isEmail(value) : true),
|
|
||||||
message: "validation_messages.invalid_email",
|
|
||||||
},
|
|
||||||
|
|
||||||
isPseudo: {
|
|
||||||
validate: (value: string) => {
|
|
||||||
const pseudoRegex = /^[a-zA-Z][a-zA-Z0-9_-]{2,19}$/;
|
|
||||||
return pseudoRegex.test(value);
|
|
||||||
},
|
|
||||||
message: "validation_messages.is_pseudo",
|
|
||||||
},
|
|
||||||
|
|
||||||
noSpaceInString: {
|
|
||||||
validate: (value: string) => {
|
|
||||||
const regex = /^\S*$/;
|
|
||||||
return regex.test(value);
|
|
||||||
},
|
|
||||||
message: "validation_messages.no_space_in_string",
|
|
||||||
},
|
|
||||||
|
|
||||||
isPositiveNumber: {
|
|
||||||
validate: (value: string) => {
|
|
||||||
let nbr = parseFloat(value);
|
|
||||||
return !(isNaN(nbr) || nbr <= 0);
|
|
||||||
},
|
|
||||||
message: "validation_messages.positive_number",
|
|
||||||
},
|
|
||||||
|
|
||||||
floatPrecision: {
|
|
||||||
validate: (value: string, precision: number) => {
|
|
||||||
// If value is not a float
|
|
||||||
if (isNaN(parseFloat(value))) return false;
|
|
||||||
let splittedValue = value.split(".");
|
|
||||||
// If there is no decimals
|
|
||||||
if (!splittedValue[1]) return true;
|
|
||||||
// If there is more decimals than the required precision
|
|
||||||
if (splittedValue[1].length > precision) return false;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
message: "validation_messages.float_precision",
|
|
||||||
},
|
|
||||||
|
|
||||||
isUrl: {
|
|
||||||
validate: (value: string, root: string | string[]) => {
|
|
||||||
try {
|
|
||||||
const url = new URL(value);
|
|
||||||
if (root) {
|
|
||||||
if (typeof root === "string") {
|
|
||||||
return url.hostname === root || url.hostname === `www.${root}`;
|
|
||||||
} else {
|
|
||||||
return root.some((r) => url.hostname === r || url.hostname === `www.${r}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
message: "validation_messages.invalid_url",
|
|
||||||
},
|
|
||||||
|
|
||||||
// isUniqueEmail: { TODO : uncomment and implement DB request
|
|
||||||
// validate: async (value: string, actual: string) => {
|
|
||||||
// try {
|
|
||||||
// const users = await AppUser.getInstance().getUsers({email: value});
|
|
||||||
// if (!users.metadata.count) return true;
|
|
||||||
// if (users.data.length > 1) return false;
|
|
||||||
// if (users.data[0]?.email === actual) return true;
|
|
||||||
// return false;
|
|
||||||
// } catch {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// message: "validation_messages.unique_email",
|
|
||||||
// },
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Validators;
|
|
||||||
export type IValidationTypes = Partial<
|
|
||||||
Record<keyof typeof Validators, boolean | Partial<{ message: string; args: any[]; isErrored: (errored: boolean) => void }>>
|
|
||||||
>;
|
|
@ -1,162 +0,0 @@
|
|||||||
import React, { ReactNode } from "react";
|
|
||||||
import BaseField, { IError, IProps as IBaseFieldProps } from "./Elements/BaseField";
|
|
||||||
|
|
||||||
export type IBaseField = BaseField<IBaseFieldProps>;
|
|
||||||
|
|
||||||
export type IFormContext = {
|
|
||||||
setField: (name: string, field: IBaseField) => void;
|
|
||||||
unSetField: (name: string) => void;
|
|
||||||
onFieldChange: (name: string, field: IBaseField) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
type IFields = {
|
|
||||||
[key: string]: IBaseField;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type IApiFormErrors = {
|
|
||||||
[fieldName: string]: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type IFormErrors = {
|
|
||||||
[key: string]: {
|
|
||||||
field: IBaseField;
|
|
||||||
errors: IError[] | undefined;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type IState = {};
|
|
||||||
export type IProps = {
|
|
||||||
onFieldChange?: (name: string, field: IBaseField, errors: IFormErrors | null) => void;
|
|
||||||
onSubmit?: (
|
|
||||||
e: React.FormEvent<HTMLFormElement> | null,
|
|
||||||
values: { [key: string]: string },
|
|
||||||
onApiErrors: (apiFormErrors: IApiFormErrors | null) => void,
|
|
||||||
) => void;
|
|
||||||
onValidated?: () => void;
|
|
||||||
onErrors?: (errors: IFormErrors) => void;
|
|
||||||
/**
|
|
||||||
* @description Url, No redirection without action
|
|
||||||
*/
|
|
||||||
action?: string;
|
|
||||||
className?: string;
|
|
||||||
children?: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FormContext = React.createContext<IFormContext>({ setField: () => {}, unSetField: () => {}, onFieldChange: () => {} });
|
|
||||||
|
|
||||||
export default class Form extends React.Component<IProps, IState> {
|
|
||||||
protected fields: IFields = {};
|
|
||||||
private formRef: React.RefObject<HTMLFormElement>;
|
|
||||||
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {};
|
|
||||||
this.setField = this.setField.bind(this);
|
|
||||||
this.unSetField = this.unSetField.bind(this);
|
|
||||||
this.onFieldChange = this.onFieldChange.bind(this);
|
|
||||||
this.onSubmit = this.onSubmit.bind(this);
|
|
||||||
this.onSubmitErrorApi = this.onSubmitErrorApi.bind(this);
|
|
||||||
this.formRef = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render() {
|
|
||||||
return (
|
|
||||||
<FormContext.Provider
|
|
||||||
value={{
|
|
||||||
setField: this.setField,
|
|
||||||
unSetField: this.unSetField,
|
|
||||||
onFieldChange: this.onFieldChange,
|
|
||||||
}}>
|
|
||||||
<form className={this.props.className} ref={this.formRef} onSubmit={this.onSubmit} action={this.props.action ?? ""}>
|
|
||||||
{this.props.children}
|
|
||||||
</form>
|
|
||||||
</FormContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onSubmit(e: React.FormEvent<HTMLFormElement> | null) {
|
|
||||||
if (!this.props.action) e?.preventDefault();
|
|
||||||
|
|
||||||
const errors = await this.validate();
|
|
||||||
|
|
||||||
if (errors) {
|
|
||||||
e?.preventDefault();
|
|
||||||
|
|
||||||
this.onErrors(errors);
|
|
||||||
|
|
||||||
return { errors };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onValidated) this.props.onValidated();
|
|
||||||
|
|
||||||
const elementsValues = this.getAllChildrenFields(e).reduce(
|
|
||||||
(obj, element) => ({ ...obj, [element.getAttribute("name") ?? ""]: (element as any).value }),
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.props.onSubmit) {
|
|
||||||
this.props.onSubmit(e, elementsValues, this.onSubmitErrorApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { values: elementsValues };
|
|
||||||
}
|
|
||||||
|
|
||||||
protected onSubmitErrorApi(apiFormErrors: IApiFormErrors | null) {
|
|
||||||
if (!apiFormErrors) return;
|
|
||||||
const errors: IFormErrors = {};
|
|
||||||
for (const [key, message] of Object.entries(apiFormErrors)) {
|
|
||||||
if (!this.fields[key]) continue;
|
|
||||||
this.fields[key]?.setErrors([
|
|
||||||
{
|
|
||||||
message,
|
|
||||||
validator: "",
|
|
||||||
value: this.fields[key]?.state.value ?? "",
|
|
||||||
args: [],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
this.onErrors(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async validate() {
|
|
||||||
const errors = (
|
|
||||||
await Promise.all(
|
|
||||||
Object.entries(this.fields).map(async ([name, field]) => {
|
|
||||||
return { name, validation: await field.validate(true), field };
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
).filter(({ validation }) => validation?.length);
|
|
||||||
|
|
||||||
if (!errors.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const errorsObject: IFormErrors = {};
|
|
||||||
errors.forEach(({ name, validation, field }) => (errorsObject[name] = { errors: validation, field }));
|
|
||||||
return errorsObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected onErrors(errors: IFormErrors) {
|
|
||||||
if (this.props.onErrors) this.props.onErrors(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected setField(name: string, field: IBaseField) {
|
|
||||||
this.fields[name] = field;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected unSetField(name: string) {
|
|
||||||
delete this.fields[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async onFieldChange(name: string, field: IBaseField) {
|
|
||||||
if (this.props.onFieldChange) {
|
|
||||||
const errors = await this.validate();
|
|
||||||
this.props.onFieldChange(name, field, errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getAllChildrenFields(e: React.FormEvent<HTMLFormElement> | null): Element[] {
|
|
||||||
return Array.from(((e?.target as HTMLFormElement) ?? this.formRef.current).elements);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: $white;
|
|
||||||
box-shadow: $shadow-nav;
|
|
||||||
padding: 24px;
|
|
||||||
position: absolute;
|
|
||||||
top: 83px;
|
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
text-align: center;
|
|
||||||
> *:not(:last-child) {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid $grey-medium;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import LogOutButton from "@Front/Components/DesignSystem/LogOutButton";
|
|
||||||
import React from "react";
|
|
||||||
import NavigationLink from "../../NavigationLink";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
isOpen: boolean;
|
|
||||||
closeModal: () => void;
|
|
||||||
};
|
|
||||||
type IState = {};
|
|
||||||
|
|
||||||
export default class BurgerModal extends React.Component<IProps, IState> {
|
|
||||||
// TODO isEnabled depending on role given by DB
|
|
||||||
public override render(): JSX.Element | null {
|
|
||||||
if (!this.props.isOpen) return null;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={classes["background"]} onClick={this.props.closeModal} />
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<NavigationLink text="Dossiers en cours" />
|
|
||||||
<NavigationLink text="Dossiers archivés" />
|
|
||||||
<NavigationLink text="Ancrage" />
|
|
||||||
<NavigationLink text="Collaborateurs" />
|
|
||||||
<NavigationLink text="Paramétrage des listes de pièces" />
|
|
||||||
<NavigationLink text="Gestion des utilisateurs" />
|
|
||||||
<NavigationLink text="Gestion des offices" />
|
|
||||||
<NavigationLink text="Gestion des noms de domaine" />
|
|
||||||
<NavigationLink text="Mon compte" />
|
|
||||||
<NavigationLink text="CGU" />
|
|
||||||
<NavigationLink path={"/design-system"} text="Design System" />
|
|
||||||
<NavigationLink path={"/"} text="Home" />
|
|
||||||
<div className={classes["separator"]} />
|
|
||||||
<LogOutButton />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
.burger-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Image from "next/image";
|
|
||||||
import BurgerIcon from "@Assets/icons/burger.svg";
|
|
||||||
import CrossIcon from "@Assets/icons/cross.svg";
|
|
||||||
import BurgerModal from "./BurgerModal";
|
|
||||||
|
|
||||||
type IProps = {};
|
|
||||||
type IState = {
|
|
||||||
isModalOpen: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class BurgerMenu extends React.Component<IProps, IState> {
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isModalOpen: false,
|
|
||||||
};
|
|
||||||
this.openModal = this.openModal.bind(this);
|
|
||||||
this.closeModal = this.closeModal.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<Image
|
|
||||||
alt="burger"
|
|
||||||
src={this.state.isModalOpen ? CrossIcon : BurgerIcon}
|
|
||||||
className={classes["burger-icon"]}
|
|
||||||
onClick={this.openModal}
|
|
||||||
/>
|
|
||||||
{this.state.isModalOpen && <BurgerModal isOpen={this.state.isModalOpen} closeModal={this.closeModal} />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private openModal() {
|
|
||||||
this.setState({ isModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
private closeModal() {
|
|
||||||
this.setState({ isModalOpen: false });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: fit-content;
|
|
||||||
margin: auto;
|
|
||||||
height: 83px;
|
|
||||||
padding: 10px 16px;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
.underline {
|
|
||||||
width: 100%;
|
|
||||||
height: 3px;
|
|
||||||
background-color: $black;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Typography, { ITypo } from "../../Typography";
|
|
||||||
import classNames from "classnames";
|
|
||||||
import router from "next/router";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
type IPropsClass = {
|
|
||||||
text: string | JSX.Element;
|
|
||||||
path?: string;
|
|
||||||
isActive?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type IStateClass = {};
|
|
||||||
|
|
||||||
class HeaderLinkClass extends React.Component<IPropsClass, IStateClass> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Link href={this.props.path ?? ""} className={classNames(classes["root"], this.props.isActive && [classes["active"]])}>
|
|
||||||
<div className={classes["content"]}>
|
|
||||||
<Typography typo={this.props.isActive ? ITypo.P_SB_18 : ITypo.NAV_HEADER_18}>{this.props.text}</Typography>
|
|
||||||
</div>
|
|
||||||
{this.props.isActive && <div className={classes["underline"]} />}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function HeaderLink(props: IPropsClass) {
|
|
||||||
const [url, setUrl] = useState("");
|
|
||||||
useEffect(() => setUrl(router?.asPath), []);
|
|
||||||
const isActive = url === props.path;
|
|
||||||
return <HeaderLinkClass {...props} isActive={isActive} />;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: inline-flex;
|
|
||||||
@media screen and (max-width: $screen-ls) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import HeaderLink from "../HeaderLink";
|
|
||||||
|
|
||||||
type IProps = {};
|
|
||||||
type IState = {};
|
|
||||||
|
|
||||||
export default class Navigation extends React.Component<IProps, IState> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<HeaderLink text={"Home"} path={"/"} />
|
|
||||||
<HeaderLink text={"Design system"} path={"/design-system"} />
|
|
||||||
<HeaderLink text={"Ancrage"} path={""} />
|
|
||||||
<HeaderLink text={"Collaborateurs"} path={""} />
|
|
||||||
<HeaderLink text={"Paramétrage des listes de pièces"} path={""} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: fit-content;
|
|
||||||
margin: auto;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
align-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Link from "next/link";
|
|
||||||
import classNames from "classnames";
|
|
||||||
import router from "next/router";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
|
||||||
|
|
||||||
type IPropsClass = {
|
|
||||||
text: string | JSX.Element;
|
|
||||||
path?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
isEnabled?: boolean;
|
|
||||||
isActive?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type IStateClass = {};
|
|
||||||
|
|
||||||
class NavigationLinkClass extends React.Component<IPropsClass, IStateClass> {
|
|
||||||
static defaultProps = { isEnabled: true };
|
|
||||||
public override render(): JSX.Element | null {
|
|
||||||
if (!this.props.isEnabled) return null;
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
href={this.props.path ?? ""}
|
|
||||||
className={classNames(classes["root"], this.props.isActive && [classes["active"]])}
|
|
||||||
onClick={this.props.onClick}>
|
|
||||||
<div className={classes["content"]}>
|
|
||||||
<Typography typo={this.props.isActive ? ITypo.P_SB_18 : ITypo.NAV_HEADER_18}>{this.props.text}</Typography>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NavigationLink(props: IPropsClass) {
|
|
||||||
const [url, setUrl] = useState("");
|
|
||||||
useEffect(() => setUrl(router?.asPath), []);
|
|
||||||
const isActive = url === props.path;
|
|
||||||
return <NavigationLinkClass {...props} isActive={isActive} />;
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 390px;
|
|
||||||
max-height: 80vh;
|
|
||||||
background-color: $white;
|
|
||||||
box-shadow: $shadow-nav;
|
|
||||||
padding: 24px;
|
|
||||||
position: absolute;
|
|
||||||
top: 107px;
|
|
||||||
right: 56px;
|
|
||||||
animation: smooth-appear 0.2s ease forwards;
|
|
||||||
|
|
||||||
@keyframes smooth-appear {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-header {
|
|
||||||
width: 100%;
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
.close-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.notification-body {
|
|
||||||
margin-top: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.missing-notification {
|
|
||||||
padding: 56px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $screen-s) {
|
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
|
|
||||||
import CloseIcon from "@Assets/icons/cross.svg";
|
|
||||||
import Image from "next/image";
|
|
||||||
import ToastHandler from "@Front/Components/DesignSystem/Toasts/ToastsHandler";
|
|
||||||
import Toasts, { IToast } from "@Front/Stores/Toasts";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
isOpen: boolean;
|
|
||||||
closeModal: () => void;
|
|
||||||
};
|
|
||||||
type IState = {
|
|
||||||
toastList: IToast[] | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class NotificationModal extends React.Component<IProps, IState> {
|
|
||||||
private removeOnToastChange: () => void = () => {};
|
|
||||||
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
toastList: Toasts.getInstance().toasts,
|
|
||||||
};
|
|
||||||
this.handleToastChange = this.handleToastChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render(): JSX.Element | null {
|
|
||||||
if (!this.props.isOpen) return null;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={classes["background"]} onClick={this.props.closeModal} />
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<div className={classes["notification-header"]}>
|
|
||||||
<Typography typo={ITypo.P_16}>Notifications</Typography>
|
|
||||||
<div className={classes["close-icon"]} onClick={this.props.closeModal}>
|
|
||||||
<Image src={CloseIcon} alt="Close notification modal" className={classes["close-icon"]}></Image>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
;
|
|
||||||
<div className={classes["notification-body"]}>
|
|
||||||
<>
|
|
||||||
{Toasts.getInstance().toasts.length === 0 ? (
|
|
||||||
<div className={classes["missing-notification"]}>
|
|
||||||
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
|
|
||||||
Vous n’avez pas de notifications.
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<ToastHandler />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentDidMount() {
|
|
||||||
this.removeOnToastChange = Toasts.getInstance().onChange(this.handleToastChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentWillUnmount() {
|
|
||||||
this.removeOnToastChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleToastChange(toastList: IToast[] | null) {
|
|
||||||
this.setState({
|
|
||||||
toastList,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
.icon-container {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.notification-icon {
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
.notification-dot {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Image from "next/image";
|
|
||||||
import NotificationIcon from "@Assets/icons/notification.svg";
|
|
||||||
import Toasts, { IToast } from "@Front/Stores/Toasts";
|
|
||||||
import NotificationModal from "./NotificationModal";
|
|
||||||
import InfoIcon from "@Assets/icons/info.svg";
|
|
||||||
|
|
||||||
type IProps = {};
|
|
||||||
type IState = {
|
|
||||||
hasNotifications: boolean;
|
|
||||||
isModalOpen: boolean;
|
|
||||||
toastList: IToast[] | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Notifications extends React.Component<IProps, IState> {
|
|
||||||
private removeOnToastChange: () => void = () => {};
|
|
||||||
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isModalOpen: false,
|
|
||||||
toastList: Toasts.getInstance().toasts, //TODO : Get from bbd
|
|
||||||
hasNotifications: Toasts.getInstance().toasts.length > 0, // TODO: Change this when we have notification stored in bbd, unread notifications
|
|
||||||
};
|
|
||||||
this.openModal = this.openModal.bind(this);
|
|
||||||
this.closeModal = this.closeModal.bind(this);
|
|
||||||
this.handleToastChange = this.handleToastChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<div className={classes["icon-container"]} onClick={this.openModal}>
|
|
||||||
<Image alt="notifications" src={NotificationIcon} className={classes["notification-icon"]} />
|
|
||||||
{this.state.hasNotifications && (
|
|
||||||
<Image className={classes["notification-dot"]} src={InfoIcon} alt="Unread notification" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{this.state.isModalOpen && <NotificationModal isOpen={this.state.isModalOpen} closeModal={this.closeModal} />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentDidMount() {
|
|
||||||
this.removeOnToastChange = Toasts.getInstance().onChange(this.handleToastChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentWillUnmount() {
|
|
||||||
this.removeOnToastChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleToastChange(toastList: IToast[] | null) {
|
|
||||||
this.setState({
|
|
||||||
toastList,
|
|
||||||
hasNotifications: toastList ? toastList.length > 0 : false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private openModal() {
|
|
||||||
this.setState({ isModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
private closeModal() {
|
|
||||||
this.setState({ isModalOpen: false });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: $white;
|
|
||||||
box-shadow: $shadow-nav;
|
|
||||||
padding: 24px;
|
|
||||||
position: absolute;
|
|
||||||
top: 107px;
|
|
||||||
right: 66px;
|
|
||||||
text-align: center;
|
|
||||||
animation: smooth-appear 0.2s ease forwards;
|
|
||||||
|
|
||||||
@keyframes smooth-appear {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> *:not(:last-child) {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid $grey-medium;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import NavigationLink from "../../NavigationLink";
|
|
||||||
import LogOutButton from "@Front/Components/DesignSystem/LogOutButton";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
isOpen: boolean;
|
|
||||||
closeModal: () => void;
|
|
||||||
};
|
|
||||||
type IState = {};
|
|
||||||
|
|
||||||
export default class ProfileModal extends React.Component<IProps, IState> {
|
|
||||||
// TODO isEnabled depending on role given by DB
|
|
||||||
public override render(): JSX.Element | null {
|
|
||||||
if (!this.props.isOpen) return null;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={classes["background"]} onClick={this.props.closeModal} />
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<NavigationLink text="Mon compte" />
|
|
||||||
<NavigationLink text="Gestion des utilisateurs" />
|
|
||||||
<NavigationLink text="Gestion des offices" />
|
|
||||||
<NavigationLink text="CGU" />
|
|
||||||
<NavigationLink path={"/design-system"} text="Design System" />
|
|
||||||
<NavigationLink path={"/"} text="Home" />
|
|
||||||
<div className={classes["separator"]} />
|
|
||||||
<LogOutButton />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
.profile-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Image from "next/image";
|
|
||||||
import ProfileIcon from "@Assets/icons/user.svg";
|
|
||||||
import ProfileModal from "./ProfileModal";
|
|
||||||
|
|
||||||
type IProps = {};
|
|
||||||
type IState = {
|
|
||||||
isModalOpen: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Profile extends React.Component<IProps, IState> {
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isModalOpen: false,
|
|
||||||
};
|
|
||||||
this.openModal = this.openModal.bind(this);
|
|
||||||
this.closeModal = this.closeModal.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<Image alt="profile" src={ProfileIcon} className={classes["profile-icon"]} onClick={this.openModal} />
|
|
||||||
{this.state.isModalOpen && <ProfileModal isOpen={this.state.isModalOpen} closeModal={this.closeModal} />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private openModal() {
|
|
||||||
this.setState({ isModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
private closeModal() {
|
|
||||||
this.setState({ isModalOpen: false });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 83px;
|
|
||||||
background-color: $white;
|
|
||||||
box-shadow: $shadow-nav;
|
|
||||||
padding: 0 48px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.logo-container {
|
|
||||||
.logo {
|
|
||||||
width: 174px;
|
|
||||||
height: 39px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-section {
|
|
||||||
.profile-section {
|
|
||||||
display: inline-flex;
|
|
||||||
> :first-child {
|
|
||||||
margin-right: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $screen-ls) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-section {
|
|
||||||
display: inline-flex;
|
|
||||||
> :first-child {
|
|
||||||
margin-right: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.burger-menu {
|
|
||||||
display: none;
|
|
||||||
@media (max-width: $screen-ls) {
|
|
||||||
display: inline-flex;
|
|
||||||
.icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Image from "next/image";
|
|
||||||
import LogoIcon from "@Assets/logo.svg";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Navigation from "./Navigation";
|
|
||||||
import Notifications from "./Notifications";
|
|
||||||
import Profile from "./Profile";
|
|
||||||
import BurgerMenu from "./BurgerMenu";
|
|
||||||
|
|
||||||
type IProps = {};
|
|
||||||
type IState = {};
|
|
||||||
|
|
||||||
export default class Header extends React.Component<IProps, IState> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<div className={classes["logo-container"]}>
|
|
||||||
<Link href="/">
|
|
||||||
<Image src={LogoIcon} alt="logo" className={classes["logo"]} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<Navigation />
|
|
||||||
<div className={classes["right-section"]}>
|
|
||||||
<div className={classes["notification-section"]}>
|
|
||||||
<Notifications />
|
|
||||||
</div>
|
|
||||||
<div className={classes["profile-section"]}>
|
|
||||||
<Profile />
|
|
||||||
</div>
|
|
||||||
<div className={classes["burger-menu"]}>
|
|
||||||
<BurgerMenu />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
@import "Themes/constants.scss";
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
animation: spin 2s linear infinite;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import LoaderIcon from "assets/images/icons/loader.svg";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Loader extends React.Component<IProps> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return <img src={LoaderIcon} className={[classes["loader"], this.props.className].filter(Boolean).join(" ")} />;
|
|
||||||
// <LoaderIcon className={[classes["loader"], this.props.className].filter(Boolean).join(" ")} />;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.disconnect-icon {
|
|
||||||
margin-left: 8px;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Image from "next/image";
|
|
||||||
import DisconnectIcon from "@Assets/icons/disconnect.svg";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Typography, { ITypo } from "../Typography";
|
|
||||||
|
|
||||||
type IProps = {};
|
|
||||||
type IState = {
|
|
||||||
isLogged: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class LogOutButton extends React.Component<IProps, IState> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["root"]} onClick={this.disconnect}>
|
|
||||||
<Typography typo={ITypo.NAV_HEADER_18}>Déconnexion</Typography>
|
|
||||||
<Image src={DisconnectIcon} className={classes["disconnect-icon"]} alt="disconnect" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private disconnect() {
|
|
||||||
console.log("disconnected");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
@import "Themes/constants.scss";
|
|
||||||
|
|
||||||
.sub-container {
|
|
||||||
padding: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
import Modal, { IModalProps } from "..";
|
|
||||||
import Button, { EButtonVariant } from "../../Button";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
type IProps = IModalProps & {
|
|
||||||
closeText: string | JSX.Element;
|
|
||||||
};
|
|
||||||
export default class Alert extends Modal<IProps> {
|
|
||||||
static defaultProps = {
|
|
||||||
closeText: "Ok",
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.onClose = this.onClose.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render(): JSX.Element | null {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
closeBtn={this.props.closeBtn}
|
|
||||||
isOpen={this.state.isOpen}
|
|
||||||
onClose={this.props.onClose}
|
|
||||||
header={this.props.header}
|
|
||||||
footer={this.footer()}>
|
|
||||||
{this.props.children}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private footer(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["button-container"]}>
|
|
||||||
<Button variant={EButtonVariant.SECONDARY} onClick={this.onClose}>
|
|
||||||
{this.props.closeText}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onClose() {
|
|
||||||
this.close();
|
|
||||||
this.props.onClose?.();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.buttons-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 16px;
|
|
||||||
margin-top: 8px;
|
|
||||||
|
|
||||||
button {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $screen-s) {
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
import Button, { EButtonVariant } from "../../Button";
|
|
||||||
import Modal, { IModalProps } from "..";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
type IProps = IModalProps & {
|
|
||||||
onAccept?: () => void;
|
|
||||||
onCancel?: () => void;
|
|
||||||
cancelText: string | JSX.Element;
|
|
||||||
confirmText: string | JSX.Element;
|
|
||||||
showCancelButton: boolean;
|
|
||||||
isConfirmButtonDisabled: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type IState = {
|
|
||||||
isOpen: boolean;
|
|
||||||
};
|
|
||||||
export default class Confirm extends Modal<IProps, IState> {
|
|
||||||
static defaultProps: Partial<IProps> = {
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelText: "Cancel",
|
|
||||||
confirmText: "Confirm",
|
|
||||||
isConfirmButtonDisabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.onCancel = this.onCancel.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render(): JSX.Element | null {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
closeBtn={this.props.closeBtn}
|
|
||||||
isOpen={this.state.isOpen}
|
|
||||||
onClose={this.props.onClose}
|
|
||||||
header={this.props.header}
|
|
||||||
footer={this.footer()}>
|
|
||||||
{this.props.children}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private footer(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["buttons-container"]}>
|
|
||||||
{this.props.showCancelButton && (
|
|
||||||
<Button variant={EButtonVariant.GHOST} onClick={this.onCancel}>
|
|
||||||
{this.props.cancelText}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button variant={EButtonVariant.PRIMARY} onClick={this.props.onAccept} disabled={this.props.isConfirmButtonDisabled}>
|
|
||||||
{this.props.confirmText}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onCancel() {
|
|
||||||
this.close();
|
|
||||||
this.props.onCancel?.();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
content: JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Footer extends React.Component<IProps> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return <footer className={classes["root"]}>{this.props.content}</footer>;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
color: var(--color-neutral-900);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
content: string | JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Header extends React.Component<IProps> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<header className={classes["root"]}>
|
|
||||||
<Typography typo={ITypo.H2}>{this.props.content}</Typography>
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 40px;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
// import Loader from "Components/Elements/Loader";
|
|
||||||
import React from "react";
|
|
||||||
import classes from "./classes.module.scss";
|
|
||||||
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
text?: string | JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class PopUpLoader extends React.Component<IProps> {
|
|
||||||
public override render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
{/* <Loader /> */}
|
|
||||||
TODO: INTEGRER LOARDER ISLOADING
|
|
||||||
<Typography typo={ITypo.P_16}>{this.props.text && this.props.text}</Typography>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|