🎨 format all documents
This commit is contained in:
parent
c4659e99e8
commit
ff0e493b67
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -4,6 +4,9 @@
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
@ -13,6 +16,9 @@
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"terminal.integrated.env.linux": {
|
||||
"PATH": "${env:HOME}/elrondsdk/vendor-rust/bin:${env:HOME}/elrondsdk/erdpy-venv/bin:${env:HOME}/elrondsdk/vmtools:${env:HOME}/elrondsdk/nodejs/latest/bin:${env:PATH}",
|
||||
"VIRTUAL_ENV": "${env:HOME}/elrondsdk/erdpy-venv",
|
||||
|
24
package-lock.json
generated
24
package-lock.json
generated
@ -42,7 +42,8 @@
|
||||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"nodemon": "^2.0.20"
|
||||
"nodemon": "^2.0.20",
|
||||
"prettier": "2.8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
@ -1439,6 +1440,21 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz",
|
||||
@ -3437,6 +3453,12 @@
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||
"dev": true
|
||||
},
|
||||
"prisma": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz",
|
||||
|
@ -7,9 +7,9 @@
|
||||
"@Front": "./dist/front/*",
|
||||
"@Assets": "./dist/front/Assets/*",
|
||||
"@Components": "./dist/front/Components/*",
|
||||
"@Themes": "./dist/front/Themes/*",
|
||||
"@Stores": "./dist/front/Stores/*",
|
||||
"@FrontServices": "./dist/front/Services/*",
|
||||
"@Themes": "./dist/front/Themes/*",
|
||||
"@Stores": "./dist/front/Stores/*",
|
||||
"@FrontServices": "./dist/front/Services/*",
|
||||
"@Pages": "./dist/pages",
|
||||
"@Common": "./dist/common",
|
||||
"@Services": "./dist/common/services",
|
||||
@ -74,7 +74,8 @@
|
||||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"nodemon": "^2.0.20"
|
||||
"nodemon": "^2.0.20",
|
||||
"prettier": "2.8.4"
|
||||
},
|
||||
"prisma": {
|
||||
"schema": "src/common/databases/schema.prisma"
|
||||
|
@ -7,7 +7,7 @@ module.exports = {
|
||||
useTabs: true,
|
||||
singleQuote: false,
|
||||
trailingComma: "all",
|
||||
printWidth: 240,
|
||||
printWidth: 140,
|
||||
semi: true,
|
||||
bracketSameLine: true,
|
||||
},
|
||||
|
@ -61,4 +61,3 @@ export default class ProjectController extends ApiController {
|
||||
this.httpCreated(res, await this.projectService.create(projectEntity));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { Container } from "typedi";
|
||||
import ProjectController from "./ProjectController";
|
||||
|
||||
export default {
|
||||
start: () => {
|
||||
Container.get(ProjectController);
|
||||
}
|
||||
}
|
||||
start: () => {
|
||||
Container.get(ProjectController);
|
||||
},
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ export default function errorHandler(error: any, req: Request, response: Respons
|
||||
if (error instanceof SyntaxError && errorStatus === 400 && "body" in error) {
|
||||
response.status(HttpCodes.BAD_REQUEST).send({
|
||||
body: error["body"],
|
||||
type: error as any ["type"],
|
||||
type: error as any["type"],
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -22,4 +22,3 @@ export default function errorHandler(error: any, req: Request, response: Respons
|
||||
|
||||
next(error);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
export default interface IDatabaseConfig {
|
||||
name: string;
|
||||
url?: string;
|
||||
}
|
||||
export default interface IDatabaseConfig {
|
||||
name: string;
|
||||
url?: string;
|
||||
}
|
||||
|
@ -26,4 +26,3 @@ export default class TezosLink {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { plainToClassFromExist } from "class-transformer";
|
||||
|
||||
export default abstract class ObjectHydrate {
|
||||
public static hydrate<T = { [key: string]: any }>(object: { [key: string]: any }, from: { [key: string]: any }): T {
|
||||
return plainToClassFromExist(object, from) as T;
|
||||
}
|
||||
}
|
||||
import { plainToClassFromExist } from "class-transformer";
|
||||
|
||||
export default abstract class ObjectHydrate {
|
||||
public static hydrate<T = { [key: string]: any }>(object: { [key: string]: any }, from: { [key: string]: any }): T {
|
||||
return plainToClassFromExist(object, from) as T;
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export default class MetricRepository {
|
||||
const data = { ...metricEntity };
|
||||
const result: MetricEntity[] = [];
|
||||
|
||||
this.instanceDb.$transaction(async(transaction: Prisma.TransactionClient) => {
|
||||
this.instanceDb.$transaction(async (transaction: Prisma.TransactionClient) => {
|
||||
for (const item of data) {
|
||||
if (!item) continue;
|
||||
result.push(
|
||||
@ -135,7 +135,7 @@ export default class MetricRepository {
|
||||
const response = this.model.groupBy({
|
||||
by: ["date_requested"],
|
||||
_count: {
|
||||
date_requested: true,
|
||||
date_requested: true,
|
||||
},
|
||||
where: {
|
||||
projectId: projectId,
|
||||
@ -187,4 +187,3 @@ export default class MetricRepository {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,14 +24,13 @@ export default class ProjectRepository {
|
||||
const data = { ...projectEntity };
|
||||
return this.model.findUnique({
|
||||
where: data,
|
||||
|
||||
|
||||
include: {
|
||||
// Include metrics & count
|
||||
Metrics: true,
|
||||
_count: {
|
||||
select: { Metrics: true },
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@ -59,4 +58,3 @@ export default class ProjectRepository {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,28 +4,28 @@ import ProjectEntity from "./ProjectEntity";
|
||||
export default class MetricEntity {
|
||||
@IsNotEmpty()
|
||||
public id!: number;
|
||||
|
||||
@IsNotEmpty(({groups: ["create"]}))
|
||||
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public path!: string;
|
||||
|
||||
@IsNotEmpty(({groups: ["create"]}))
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public uuid!: string;
|
||||
|
||||
@IsNotEmpty(({groups: ["create"]}))
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public remote_address!: string;
|
||||
|
||||
@IsNotEmpty(({groups: ["create"]}))
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public date_requested!: Date;
|
||||
|
||||
@IsNotEmpty(({groups: ["create"]}))
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public projectId!: number;
|
||||
|
||||
@IsNotEmpty(({groups: ["create"]}))
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public project!: ProjectEntity;
|
||||
|
||||
@IsDate()
|
||||
public createdAt?: Date;
|
||||
|
||||
@IsDate()
|
||||
@IsDate()
|
||||
public updatedAt?: Date;
|
||||
}
|
||||
|
@ -5,21 +5,21 @@ export default class ProjectEntity {
|
||||
@IsNotEmpty()
|
||||
public id!: number;
|
||||
|
||||
@IsNotEmpty({groups: ["create"]})
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public title!: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNotEmpty()
|
||||
public uuid!: string;
|
||||
|
||||
@IsDate()
|
||||
public createdAt!: Date;
|
||||
|
||||
@IsDate()
|
||||
@IsDate()
|
||||
public updatedAt!: Date;
|
||||
|
||||
@IsNotEmpty({groups: ["create"]})
|
||||
@IsNotEmpty({ groups: ["create"] })
|
||||
public network!: string;
|
||||
|
||||
@IsOptional()
|
||||
public metrics?: MetricEntity[];
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import ProjectEntity from "./ProjectEntity";
|
||||
import MetricEntity from "./MetricEntity";
|
||||
|
||||
export { ProjectEntity, MetricEntity};
|
||||
import ProjectEntity from "./ProjectEntity";
|
||||
import MetricEntity from "./MetricEntity";
|
||||
|
||||
export { ProjectEntity, MetricEntity };
|
||||
|
@ -1,2 +1 @@
|
||||
export default class BaseService {}
|
||||
|
||||
|
@ -35,18 +35,18 @@ export default class MetricService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws {Error} If metric is undefined
|
||||
* @returns
|
||||
*/
|
||||
public async getCountRpcPath(metricEntity: Partial<MetricEntity>, from: Date, to: Date) {
|
||||
const pathsCount = await this.metricRepository.countRpcPathUsage(metricEntity.projectId!,from,to);
|
||||
if (!pathsCount) return null;
|
||||
return pathsCount;
|
||||
}
|
||||
*
|
||||
* @throws {Error} If metric is undefined
|
||||
* @returns
|
||||
*/
|
||||
public async getCountRpcPath(metricEntity: Partial<MetricEntity>, from: Date, to: Date) {
|
||||
const pathsCount = await this.metricRepository.countRpcPathUsage(metricEntity.projectId!, from, to);
|
||||
if (!pathsCount) return null;
|
||||
return pathsCount;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @throws {Error} If metric is undefined
|
||||
*
|
||||
* @throws {Error} If metric is undefined
|
||||
* @returns
|
||||
*/
|
||||
public async getCountAllMetrics(metricEntity: Partial<MetricEntity>) {
|
||||
@ -56,27 +56,27 @@ export default class MetricService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @throws {Error} If metric is undefined
|
||||
* @returns
|
||||
*/
|
||||
public async getLastMetrics(metricEntity: Partial<MetricEntity>, limit: number){
|
||||
const lastMetric = await this.metricRepository.findLastRequests(metricEntity.projectId!,limit);
|
||||
public async getLastMetrics(metricEntity: Partial<MetricEntity>, limit: number) {
|
||||
const lastMetric = await this.metricRepository.findLastRequests(metricEntity.projectId!, limit);
|
||||
return ObjectHydrate.hydrate<Partial<MetricEntity>>(new MetricEntity(), lastMetric);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @throws {Error} If metric is undefined
|
||||
* @returns
|
||||
*/
|
||||
public async getRequestsByDay(metricEntity: Partial<MetricEntity>, from: Date, to: Date){
|
||||
const requestByDay = await this.metricRepository.findRequestsByDay(metricEntity.projectId!,from,to);
|
||||
public async getRequestsByDay(metricEntity: Partial<MetricEntity>, from: Date, to: Date) {
|
||||
const requestByDay = await this.metricRepository.findRequestsByDay(metricEntity.projectId!, from, to);
|
||||
return ObjectHydrate.hydrate<Partial<MetricEntity>>(new MetricEntity(), requestByDay);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @throws {Error} If metric is undefined
|
||||
* @returns
|
||||
*/
|
||||
@ -85,4 +85,3 @@ export default class MetricService {
|
||||
await this.metricRepository.removeOldMetricsBymonths(months);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,6 @@ import { ProjectEntity } from "@Common/ressources";
|
||||
import { type processFindManyQuery } from "prisma-query";
|
||||
import { Service } from "typedi";
|
||||
|
||||
|
||||
|
||||
@Service()
|
||||
export default class ProjectService {
|
||||
constructor(private projectRepository: ProjectRepository) {}
|
||||
@ -17,7 +15,7 @@ export default class ProjectService {
|
||||
const projects = await this.projectRepository.findMany(query);
|
||||
return ObjectHydrate.hydrate<Partial<ProjectEntity>>(new ProjectEntity(), projects);
|
||||
}
|
||||
/**
|
||||
/**
|
||||
* @throws {Error} If project is undefined
|
||||
*/
|
||||
public async getByUUID(projectEntity: Partial<ProjectEntity>) {
|
||||
@ -25,15 +23,14 @@ export default class ProjectService {
|
||||
if (!project) return null;
|
||||
return ObjectHydrate.hydrate<Partial<ProjectEntity>>(new ProjectEntity(), project);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @throws {Error} If project cannot be created
|
||||
* @returns
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @throws {Error} If project cannot be created
|
||||
* @returns
|
||||
*/
|
||||
public async create(projectEntity: Partial<ProjectEntity>) {
|
||||
const project = await this.projectRepository.create(projectEntity);
|
||||
if (!project) return null;
|
||||
return ObjectHydrate.hydrate<Partial<ProjectEntity>>(new ProjectEntity(), project);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,4 +31,3 @@ export default class ExpressServer implements ServerInterface {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,4 +14,3 @@ export default interface ServerInterface {
|
||||
|
||||
init(config: IConfig): this;
|
||||
}
|
||||
|
||||
|
@ -6,4 +6,3 @@ import HttpCodes from "@Common/system/controller-pattern/HttpCodes";
|
||||
export default abstract class ApiController extends BaseController {}
|
||||
|
||||
export { HttpCodes as ResponseStatusCodes };
|
||||
|
||||
|
@ -2,10 +2,8 @@ import { StRoute } from "./StRoute";
|
||||
import { Response } from "express";
|
||||
import HttpCodes from "@Common/system/controller-pattern/HttpCodes";
|
||||
|
||||
|
||||
type IResponseData = {} | string | number | boolean | null | unknown;
|
||||
|
||||
|
||||
export default abstract class BaseController {
|
||||
public expressRoutes!: StRoute[];
|
||||
public httpCode: typeof HttpCodes = HttpCodes;
|
||||
|
@ -34,4 +34,3 @@ function createRoute(controller: any, route: StRoute) {
|
||||
}
|
||||
|
||||
export default Controller;
|
||||
|
||||
|
@ -12,4 +12,3 @@ export default class ErrorCatch {
|
||||
next(args[args.length - 1] ?? "Unknown Error");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
enum HttpCodes {
|
||||
SUCCESS = 200,
|
||||
CREATED = 201,
|
||||
BAD_REQUEST = 400,
|
||||
INTERNAL_ERROR = 500,
|
||||
UNKNOWN_ERROR = 520,
|
||||
NOT_IMPLEMENTED = 501,
|
||||
NOT_FOUND = 404,
|
||||
}
|
||||
export default HttpCodes;
|
||||
enum HttpCodes {
|
||||
SUCCESS = 200,
|
||||
CREATED = 201,
|
||||
BAD_REQUEST = 400,
|
||||
INTERNAL_ERROR = 500,
|
||||
UNKNOWN_ERROR = 520,
|
||||
NOT_IMPLEMENTED = 501,
|
||||
NOT_FOUND = 404,
|
||||
}
|
||||
export default HttpCodes;
|
||||
|
@ -1,7 +1,12 @@
|
||||
import BaseController from "./BaseController";
|
||||
import { StRoute } from "./StRoute";
|
||||
|
||||
function MethodsAny(type: StRoute["type"], path: string, frontMiddlewares: StRoute["frontMiddlewares"] = [], backMiddlewares: StRoute["backMiddlewares"] = []) {
|
||||
function MethodsAny(
|
||||
type: StRoute["type"],
|
||||
path: string,
|
||||
frontMiddlewares: StRoute["frontMiddlewares"] = [],
|
||||
backMiddlewares: StRoute["backMiddlewares"] = [],
|
||||
) {
|
||||
return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
|
||||
const func = propertyDescriptor.value;
|
||||
const constructor: typeof BaseController = target.constructor;
|
||||
@ -29,4 +34,3 @@ export const Delete = MethodsAny.bind(null, "delete");
|
||||
* @description Decorator Method PUT
|
||||
*/
|
||||
export const Put = MethodsAny.bind(null, "put");
|
||||
|
||||
|
@ -7,4 +7,3 @@ export interface StRoute {
|
||||
frontMiddlewares: ((requests: Request, response: Response, next: NextFunction) => void)[];
|
||||
backMiddlewares: ((requests: Request, response: Response, next: NextFunction) => void)[];
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import HttpCodes from "../HttpCodes";
|
||||
|
||||
export default class HttpException extends Error {
|
||||
constructor(message: string, public httpCode: HttpCodes = HttpCodes.UNKNOWN_ERROR) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
import HttpCodes from "../HttpCodes";
|
||||
|
||||
export default class HttpException extends Error {
|
||||
constructor(message: string, public httpCode: HttpCodes = HttpCodes.UNKNOWN_ERROR) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,31 @@
|
||||
import dotenv from "dotenv";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import IDatabaseConfig from "../../config/IDatabaseConfig";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export default class DbProvider {
|
||||
protected client = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: `postgres://${process.env["DATABASE_USER"]}:${process.env["DATABASE_PASSWORD"]}@${process.env["DATABASE_HOSTNAME"]}:${process.env["DATABASE_PORT"]}/${process.env["DATABASE_NAME"]}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
constructor(protected config: IDatabaseConfig) {
|
||||
}
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
await this.client.$connect();
|
||||
console.info(`⚡️[Prisma]: Connected to ${this.config.name}`);// A Logger middleware is to be added here
|
||||
}
|
||||
|
||||
public getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public async disconnect(): Promise<void> {
|
||||
await this.client.$disconnect();
|
||||
console.info(`⚡️[Prisma]: Disconnected from ${this.config.name}`); // A Logger middleware is to be added here
|
||||
}
|
||||
}
|
||||
import dotenv from "dotenv";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import IDatabaseConfig from "../../config/IDatabaseConfig";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export default class DbProvider {
|
||||
protected client = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: `postgres://${process.env["DATABASE_USER"]}:${process.env["DATABASE_PASSWORD"]}@${process.env["DATABASE_HOSTNAME"]}:${process.env["DATABASE_PORT"]}/${process.env["DATABASE_NAME"]}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
constructor(protected config: IDatabaseConfig) {}
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
await this.client.$connect();
|
||||
console.info(`⚡️[Prisma]: Connected to ${this.config.name}`); // A Logger middleware is to be added here
|
||||
}
|
||||
|
||||
public getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public async disconnect(): Promise<void> {
|
||||
await this.client.$disconnect();
|
||||
console.info(`⚡️[Prisma]: Disconnected from ${this.config.name}`); // A Logger middleware is to be added here
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,4 @@ export class ORMBadQueryError extends Error {
|
||||
constructor(message: string, public error: Error) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,4 @@ import IDatabaseConfig from "@Common/config/IDatabaseConfig";
|
||||
import DbProvider from "./DbProvider";
|
||||
|
||||
export type { IDatabaseConfig };
|
||||
export default DbProvider;
|
||||
export default DbProvider;
|
||||
|
@ -22,15 +22,8 @@ Container.get(ExpressServer).init({
|
||||
label,
|
||||
port: parseInt(port),
|
||||
rootUrl,
|
||||
middlwares: [
|
||||
cors({ origin: "*" }),
|
||||
bodyParser.urlencoded({ extended: true }),
|
||||
bodyParser.json(),
|
||||
],
|
||||
middlwares: [cors({ origin: "*" }), bodyParser.urlencoded({ extended: true }), bodyParser.json()],
|
||||
errorHandler,
|
||||
});
|
||||
|
||||
routes.start();
|
||||
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ if (!rootUrl) throw new Error(`process.env RootUrl is undefined`);
|
||||
|
||||
Container.get(NextServer).init({
|
||||
label,
|
||||
isDev: process.env.NODE_ENV !== 'production',
|
||||
isDev: process.env.NODE_ENV !== "production",
|
||||
port: parseInt(port),
|
||||
rootUrl,
|
||||
});
|
||||
});
|
||||
|
@ -1,14 +1,17 @@
|
||||
|
||||
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'];
|
||||
|
||||
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() {}
|
||||
|
||||
@ -114,7 +117,6 @@ export default abstract class BaseApiService {
|
||||
protected onError(error: unknown) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface IResponse {
|
||||
|
@ -1,55 +1,55 @@
|
||||
import BaseApiService from "src/front/Api/BaseApiService"
|
||||
import { IProject } from "src/front/interfaces"
|
||||
import BaseApiService from "src/front/Api/BaseApiService";
|
||||
import { IProject } from "src/front/interfaces";
|
||||
import { Service } from "typedi";
|
||||
|
||||
type IPostProject = {
|
||||
title: string,
|
||||
network: string
|
||||
}
|
||||
title: string;
|
||||
network: string;
|
||||
};
|
||||
@Service()
|
||||
export default class Project extends BaseApiService {
|
||||
private static instance: Project;
|
||||
private readonly baseURl = this.backUrl.concat('/projects');
|
||||
private static instance: Project;
|
||||
private readonly baseURl = this.backUrl.concat("/projects");
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
}
|
||||
private constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!this.instance) {
|
||||
return new Project();
|
||||
} else {
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
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 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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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,101 +1,101 @@
|
||||
@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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
&[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;
|
||||
}
|
||||
&:hover {
|
||||
border-color: $purple-hover;
|
||||
background-color: $purple-hover;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
border-color: $purple-soft;
|
||||
background-color: $purple-soft;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
&: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;
|
||||
&[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;
|
||||
}
|
||||
&:hover {
|
||||
border-color: $re-hover;
|
||||
background-color: $re-hover;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
border-color: $red-soft;
|
||||
background-color: $red-soft;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
&: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;
|
||||
&[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;
|
||||
}
|
||||
}
|
||||
svg {
|
||||
path {
|
||||
stroke: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $pink-hover;
|
||||
color: $pink-hover;
|
||||
}
|
||||
&:hover {
|
||||
border-color: $pink-hover;
|
||||
color: $pink-hover;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
border-color: $pink-soft;
|
||||
background-color: $pink-soft;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
&:disabled {
|
||||
border-color: $pink-soft;
|
||||
background-color: $pink-soft;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[fullwidth="true"] {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
&[fullwidth="true"] {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&[touppercase="false"] {
|
||||
text-transform: inherit;
|
||||
}
|
||||
&[touppercase="false"] {
|
||||
text-transform: inherit;
|
||||
}
|
||||
}
|
||||
|
@ -2,49 +2,44 @@ import React from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
export enum EButtonVariant {
|
||||
PRIMARY = "primary",
|
||||
SECONDARY = "secondary",
|
||||
GHOST = "ghost",
|
||||
LINE = "line",
|
||||
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;
|
||||
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",
|
||||
};
|
||||
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>
|
||||
);
|
||||
}
|
||||
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,39 +1,38 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input[type="checkbox"] {
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid $green-flash;
|
||||
border-radius: 2px;
|
||||
margin-right: 16px;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid $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: $green-flash;
|
||||
border-radius: 2px;
|
||||
transform: scale(0);
|
||||
}
|
||||
input[type="checkbox"]::before {
|
||||
content: url("../../../assets/icons/check.svg");
|
||||
place-content: center;
|
||||
display: grid;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: $green-flash;
|
||||
border-radius: 2px;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
margin-left: 16px;
|
||||
}
|
||||
input[type="checkbox"]:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
@ -4,27 +4,24 @@ import Typography, { ITypo, ITypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {
|
||||
name: string;
|
||||
toolTip?: string;
|
||||
name: string;
|
||||
toolTip?: string;
|
||||
};
|
||||
|
||||
export default class CheckBox extends React.Component<IProps> {
|
||||
static defaultProps = {
|
||||
toolTip: "",
|
||||
};
|
||||
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>
|
||||
);
|
||||
}
|
||||
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 +1,154 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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,87 +1,87 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
position: relative;
|
||||
|
||||
.input {
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 24px;
|
||||
gap: 10px;
|
||||
.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;
|
||||
width: 530px;
|
||||
height: 70px;
|
||||
border: 1px solid $grey-medium;
|
||||
|
||||
&:focus {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
}
|
||||
&:not([value=""]) {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform;
|
||||
}
|
||||
}
|
||||
&:focus {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
}
|
||||
&:not([value=""]) {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform;
|
||||
}
|
||||
}
|
||||
|
||||
&[type="number"] {
|
||||
&:focus {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
}
|
||||
&:not([value=""]) {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
&[type="number"] {
|
||||
&:focus {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
}
|
||||
&:not([value=""]) {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not([value=""]) {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
}
|
||||
&:not([value=""]) {
|
||||
~ .fake-placeholder {
|
||||
transform: translateY(-35px);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
}
|
||||
|
||||
~ .fake-placeholder {
|
||||
z-index: 2;
|
||||
top: 35%;
|
||||
margin-left: 8px;
|
||||
padding: 0 16px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
background: $white;
|
||||
}
|
||||
}
|
||||
~ .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;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 24px;
|
||||
gap: 10px;
|
||||
|
||||
width: 530px;
|
||||
height: 70px;
|
||||
border: 1px solid $grey-medium;
|
||||
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);
|
||||
}
|
||||
~ .fake-placeholder {
|
||||
z-index: 2;
|
||||
top: -12px;
|
||||
margin-left: 8px;
|
||||
padding: 0 16px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
background: $white;
|
||||
// transform: translateY(-35px);
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,9 @@ export default class InputField extends BaseField<IProps> {
|
||||
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"]}
|
||||
className={
|
||||
this.props.className ? [classes["textarea"], classes[this.props.className]].join(" ") : classes["textarea"]
|
||||
}
|
||||
/>
|
||||
<div className={classes["fake-placeholder"]}>{this.props.fakeplaceholder}</div>
|
||||
</div>
|
||||
@ -61,7 +63,9 @@ export default class InputField extends BaseField<IProps> {
|
||||
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"]}
|
||||
className={
|
||||
this.props.className ? [classes["input"], classes[this.props.className]].join(" ") : classes["input"]
|
||||
}
|
||||
/>
|
||||
<div className={classes["fake-placeholder"]}>{this.props.fakeplaceholder}</div>
|
||||
</div>
|
||||
|
@ -1,180 +1,180 @@
|
||||
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 }>>
|
||||
>;
|
||||
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,22 +1,22 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
height: 83px;
|
||||
padding: 10px 16px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
margin: auto;
|
||||
}
|
||||
.underline {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background-color: $black;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
@ -4,33 +4,32 @@ import Link from "next/link";
|
||||
import Typography, { ITypo } from "../../Typography";
|
||||
import classNames from "classnames";
|
||||
import router from "next/router";
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type IPropsClass = {
|
||||
text: string | JSX.Element;
|
||||
path?: string;
|
||||
isActive?: boolean;
|
||||
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={ITypo.NAV_HEADER_18}>
|
||||
{this.props.text}
|
||||
</Typography>
|
||||
</div>
|
||||
{this.props.isActive && <div className={classes["underline"]} />}
|
||||
</Link>;
|
||||
}
|
||||
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={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} />;
|
||||
const [url, setUrl] = useState("");
|
||||
useEffect(() => setUrl(router?.asPath), []);
|
||||
const isActive = url === props.path;
|
||||
return <HeaderLinkClass {...props} isActive={isActive} />;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: inline-flex;
|
||||
@media screen and (max-width: $screen-ls) {
|
||||
display: none;
|
||||
}
|
||||
display: inline-flex;
|
||||
@media screen and (max-width: $screen-ls) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,15 @@ 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>;
|
||||
}
|
||||
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,37 +1,37 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 300px;
|
||||
max-height: 80%;
|
||||
background-color: $white;
|
||||
box-shadow: $shadow-nav;
|
||||
padding: 24px;
|
||||
position: absolute;
|
||||
top: 107px;
|
||||
right:56px;
|
||||
|
||||
.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;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 300px;
|
||||
max-height: 80%;
|
||||
background-color: $white;
|
||||
box-shadow: $shadow-nav;
|
||||
padding: 24px;
|
||||
position: absolute;
|
||||
top: 107px;
|
||||
right: 56px;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.background{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
}
|
||||
.background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
@ -7,63 +7,64 @@ import ToastHandler from "@Front/Components/DesignSystem/Toasts/ToastsHandler";
|
||||
import Toasts, { IToast } from "@Front/Stores/Toasts";
|
||||
|
||||
type IProps = {
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
};
|
||||
type IState = {
|
||||
toastList: IToast[] | null;
|
||||
toastList: IToast[] | null;
|
||||
};
|
||||
|
||||
export default class NotificationModal extends React.Component<IProps, IState> {
|
||||
private removeOnToastChange: () => void = () => { };
|
||||
private removeOnToastChange: () => void = () => {};
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
toastList: Toasts.getInstance().toasts,
|
||||
};
|
||||
this.handleToastChange = this.handleToastChange.bind(this);
|
||||
}
|
||||
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>;
|
||||
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>
|
||||
<Typography typo={ITypo.P_16}>No notification yet</Typography>
|
||||
</div>
|
||||
) : (
|
||||
<ToastHandler />
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
<div className={classes["notification-body"]}>
|
||||
<>
|
||||
{Toasts.getInstance().toasts.length === 0
|
||||
?
|
||||
<div>
|
||||
<Typography typo={ITypo.P_16}>No notification yet</Typography>
|
||||
</div>
|
||||
: <ToastHandler />}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
public override componentDidMount() {
|
||||
this.removeOnToastChange = Toasts.getInstance().onChange(this.handleToastChange);
|
||||
}
|
||||
|
||||
public override componentDidMount() {
|
||||
this.removeOnToastChange = Toasts.getInstance().onChange(this.handleToastChange);
|
||||
}
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnToastChange();
|
||||
}
|
||||
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnToastChange();
|
||||
}
|
||||
|
||||
private handleToastChange(toastList: IToast[] | null) {
|
||||
this.setState({
|
||||
toastList,
|
||||
});
|
||||
}
|
||||
private handleToastChange(toastList: IToast[] | null) {
|
||||
this.setState({
|
||||
toastList,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
.icon-container {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.notification-icon {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
.notification-dot {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,60 +8,61 @@ import InfoIcon from "@Assets/icons/info.svg";
|
||||
|
||||
type IProps = {};
|
||||
type IState = {
|
||||
hasNotifications: boolean;
|
||||
isModalOpen: boolean;
|
||||
toastList: IToast[] | null;
|
||||
hasNotifications: boolean;
|
||||
isModalOpen: boolean;
|
||||
toastList: IToast[] | null;
|
||||
};
|
||||
|
||||
export default class Notifications extends React.Component<IProps, IState> {
|
||||
private removeOnToastChange: () => void = () => { };
|
||||
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);
|
||||
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>
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
{this.state.isModalOpen && <NotificationModal isOpen={this.state.isModalOpen} closeModal={this.closeModal} />}
|
||||
</div>;
|
||||
}
|
||||
public override componentDidMount() {
|
||||
this.removeOnToastChange = Toasts.getInstance().onChange(this.handleToastChange);
|
||||
}
|
||||
|
||||
public override componentDidMount() {
|
||||
this.removeOnToastChange = Toasts.getInstance().onChange(this.handleToastChange);
|
||||
}
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnToastChange();
|
||||
}
|
||||
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnToastChange();
|
||||
}
|
||||
private handleToastChange(toastList: IToast[] | null) {
|
||||
this.setState({
|
||||
toastList,
|
||||
hasNotifications: toastList ? toastList.length > 0 : false,
|
||||
});
|
||||
}
|
||||
|
||||
private handleToastChange(toastList: IToast[] | null) {
|
||||
this.setState({
|
||||
toastList,
|
||||
hasNotifications: toastList ? toastList.length > 0 : false
|
||||
});
|
||||
}
|
||||
private openModal() {
|
||||
this.setState({ isModalOpen: true });
|
||||
}
|
||||
|
||||
private openModal() {
|
||||
this.setState({ isModalOpen: true });
|
||||
};
|
||||
|
||||
private closeModal() {
|
||||
this.setState({ isModalOpen: false });
|
||||
};
|
||||
}
|
||||
private closeModal() {
|
||||
this.setState({ isModalOpen: false });
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
|
||||
.content{
|
||||
align-content: center;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
align-content: center;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.profile-icon{
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
.profile-icon {
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
@ -6,33 +6,33 @@ import ProfileModal from "./ProfileModal";
|
||||
|
||||
type IProps = {};
|
||||
type IState = {
|
||||
isModalOpen: boolean;
|
||||
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);
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
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 });
|
||||
};
|
||||
private closeModal() {
|
||||
this.setState({ isModalOpen: false });
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
@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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
.logo-container {
|
||||
.logo {
|
||||
width: 174px;
|
||||
height: 39px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-section {
|
||||
display: inline-flex;
|
||||
>:first-child{
|
||||
margin-right: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right-section {
|
||||
display: inline-flex;
|
||||
> :first-child {
|
||||
margin-right: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import Image from "next/image";
|
||||
import LogoIcon from "@Assets/logo.svg"
|
||||
import LogoIcon from "@Assets/logo.svg";
|
||||
import Link from "next/link";
|
||||
import Navigation from "./Navigation";
|
||||
import Notifications from "./Notifications";
|
||||
@ -11,18 +11,20 @@ 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"]}>
|
||||
<Notifications/>
|
||||
<Profile />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
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"]}>
|
||||
<Notifications />
|
||||
<Profile />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ interface IProps {
|
||||
|
||||
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(" ")} />
|
||||
return <img src={LoaderIcon} className={[classes["loader"], this.props.className].filter(Boolean).join(" ")} />;
|
||||
// <LoaderIcon className={[classes["loader"], this.props.className].filter(Boolean).join(" ")} />;
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {};
|
||||
type IState = {
|
||||
logged: boolean;
|
||||
logged: boolean;
|
||||
};
|
||||
|
||||
export default class Login extends React.Component<IProps, IState> {
|
||||
public override render(): JSX.Element {
|
||||
return <div className={classes["root"]}></div>;
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return <div className={classes["root"]}></div>;
|
||||
}
|
||||
}
|
||||
|
@ -11,4 +11,4 @@
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
@import "Themes/constants.scss";
|
||||
|
||||
.sub-container{
|
||||
.sub-container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.button-container{
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content:flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
@ -49,10 +49,7 @@ export default class Confirm extends Modal<IProps, IState> {
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant={EButtonVariant.PRIMARY}
|
||||
onClick={this.props.onAccept}
|
||||
disabled={this.props.isConfirmButtonDisabled}>
|
||||
<Button variant={EButtonVariant.PRIMARY} onClick={this.props.onAccept} disabled={this.props.isConfirmButtonDisabled}>
|
||||
{this.props.confirmText}
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -10,4 +10,3 @@
|
||||
color: var(--color-neutral-900);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,7 @@ 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>
|
||||
<Typography typo={ITypo.H2}>{this.props.content}</Typography>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
@ -13,9 +13,7 @@ export default class PopUpLoader extends React.Component<IProps> {
|
||||
<div className={classes["root"]}>
|
||||
{/* <Loader /> */}
|
||||
TODO: INTEGRER LOARDER ISLOADING
|
||||
<Typography typo={ITypo.P_16}>
|
||||
{this.props.text && this.props.text}
|
||||
</Typography>
|
||||
<Typography typo={ITypo.P_16}>{this.props.text && this.props.text}</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -95,4 +95,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import Header from "./Elements/Header";
|
||||
|
||||
import Loader from "./Elements/Loader";
|
||||
import Typography, { ITypo } from "../Typography";
|
||||
import CrossIcon from "@Assets/icons/cross.svg"
|
||||
import CrossIcon from "@Assets/icons/cross.svg";
|
||||
import Image from "next/image";
|
||||
|
||||
export type IModalProps = {
|
||||
@ -29,7 +29,7 @@ export default class Modal<P extends IModalProps, S extends IState = IState> ext
|
||||
constructor(props: P) {
|
||||
super(props);
|
||||
this.close = this.close.bind(this);
|
||||
|
||||
|
||||
(this.state as any) = {
|
||||
isOpen: props.isOpen ?? true,
|
||||
};
|
||||
@ -45,17 +45,15 @@ export default class Modal<P extends IModalProps, S extends IState = IState> ext
|
||||
className={[classes["container"], this.props.hasTransparentBackground && classes["transparant-background"]].join(" ")}
|
||||
onClick={this.props.hasContainerClosable && this.close}>
|
||||
{this.props.closeBtn && (
|
||||
<div className={classes["cross"]} >
|
||||
<Image alt="Unplugged" src={CrossIcon} className={classes["close-icon"]} onClick={this.close}/>
|
||||
<div className={classes["cross"]}>
|
||||
<Image alt="Unplugged" src={CrossIcon} className={classes["close-icon"]} onClick={this.close} />
|
||||
</div>
|
||||
)}
|
||||
<div className={classes["sub-container"]}>
|
||||
{this.props.header && <Header content={this.props.header} />}
|
||||
|
||||
<Typography typo={ITypo.P_16}>
|
||||
<>
|
||||
{this.props.children ? this.props.children : <Loader text={this.props.textLoader} />}
|
||||
</>
|
||||
<>{this.props.children ? this.props.children : <Loader text={this.props.textLoader} />}</>
|
||||
</Typography>
|
||||
{this.props.children && this.props.footer && <Footer content={this.props.footer} />}
|
||||
</div>
|
||||
|
@ -1,39 +1,39 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input[type="radio"] {
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid $green-flash;
|
||||
border-radius: 100px;
|
||||
margin-right: 16px;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
input[type="radio"] {
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid $green-flash;
|
||||
border-radius: 100px;
|
||||
margin-right: 16px;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
input[type="radio"]::before {
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: $green-flash;
|
||||
border-radius: 100px;
|
||||
transform: scale(0);
|
||||
}
|
||||
input[type="radio"]::before {
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: $green-flash;
|
||||
border-radius: 100px;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
input[type="radio"]:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
margin-left: 16px;
|
||||
}
|
||||
.tooltip {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
@ -4,25 +4,21 @@ import Typography, { ITypo, ITypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {
|
||||
children: React.ReactNode;
|
||||
name: string;
|
||||
toolTip?: string;
|
||||
children: React.ReactNode;
|
||||
name: string;
|
||||
toolTip?: string;
|
||||
};
|
||||
|
||||
export default class RadioBox extends React.Component<IProps> {
|
||||
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Typography typo={ITypo.P_ERR_16} color={ITypoColor.BLACK}>
|
||||
<label className={classes["root"]}>
|
||||
<input
|
||||
type="radio"
|
||||
name={this.props.name}
|
||||
/>
|
||||
{this.props.children}
|
||||
{this.props.toolTip && <Tooltip className={classes["tooltip"]} text={this.props.toolTip}/>}
|
||||
</label>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Typography typo={ITypo.P_ERR_16} color={ITypoColor.BLACK}>
|
||||
<label className={classes["root"]}>
|
||||
<input type="radio" name={this.props.name} />
|
||||
{this.props.children}
|
||||
{this.props.toolTip && <Tooltip className={classes["tooltip"]} text={this.props.toolTip} />}
|
||||
</label>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,96 +1,96 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
@keyframes slide-left {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(120%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(0%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-right {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate(120%);
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
padding: 24px;
|
||||
background: $orange-soft;
|
||||
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.11);
|
||||
border-radius: 5px;
|
||||
|
||||
animation-name: slide-left;
|
||||
animation-duration: 400ms;
|
||||
animation-timing-function: $custom-easing;
|
||||
animation-fill-mode: forwards;
|
||||
|
||||
&[data-will-close="true"] {
|
||||
animation-duration: 200ms;
|
||||
animation-name: slide-right;
|
||||
animation-timing-function: $custom-easing;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.loadbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
background: var(--color-neutral-500);
|
||||
border-radius: 5px;
|
||||
transform-origin: right;
|
||||
transform: scaleX(0);
|
||||
animation: loadbar-animation var(--data-duration) linear;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-family: var(--font-primary);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: var(--color-neutral-900);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.cross {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.text-icon_row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
p {
|
||||
padding-left: 14px;
|
||||
width: 150%;
|
||||
}
|
||||
}
|
||||
|
||||
.text-container {
|
||||
font-family: var(--font-primary);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--color-neutral-500);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
@keyframes slide-left {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(120%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(0%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-right {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate(120%);
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
padding: 24px;
|
||||
background: $orange-soft;
|
||||
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.11);
|
||||
border-radius: 5px;
|
||||
|
||||
animation-name: slide-left;
|
||||
animation-duration: 400ms;
|
||||
animation-timing-function: $custom-easing;
|
||||
animation-fill-mode: forwards;
|
||||
|
||||
&[data-will-close="true"] {
|
||||
animation-duration: 200ms;
|
||||
animation-name: slide-right;
|
||||
animation-timing-function: $custom-easing;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.loadbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
background: var(--color-neutral-500);
|
||||
border-radius: 5px;
|
||||
transform-origin: right;
|
||||
transform: scaleX(0);
|
||||
animation: loadbar-animation var(--data-duration) linear;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-family: var(--font-primary);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: var(--color-neutral-900);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.cross {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.text-icon_row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
p {
|
||||
padding-left: 14px;
|
||||
width: 150%;
|
||||
}
|
||||
}
|
||||
|
||||
.text-container {
|
||||
font-family: var(--font-primary);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--color-neutral-500);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
@ -1,102 +1,98 @@
|
||||
import React from "react";
|
||||
|
||||
// Components
|
||||
|
||||
// Stores
|
||||
|
||||
// Styles
|
||||
import classes from "./classes.module.scss";
|
||||
import Toasts, { IToast } from "@Front/Stores/Toasts";
|
||||
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
|
||||
type IProps = {
|
||||
toast: IToast;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
willClose: boolean;
|
||||
};
|
||||
|
||||
export default class ToastElement extends React.Component<IProps, IState> {
|
||||
private closeTimeout = 0;
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
willClose: false,
|
||||
};
|
||||
|
||||
this.onClose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
const toast = this.props.toast;
|
||||
const style = {
|
||||
"--data-duration": `${toast.time}ms`,
|
||||
} as React.CSSProperties;
|
||||
return (
|
||||
<div className={classes["root"]} data-will-close={this.state.willClose}>
|
||||
{toast.time !== 0 && <div className={classes["loadbar"]} style={style} />}
|
||||
<div className={classes["header"]}>
|
||||
<div className={classes["text-icon_row"]}>
|
||||
{toast.icon && toast.icon}
|
||||
<div className={classes["text-container"]}>
|
||||
{this.getToastTitle(toast.title)}
|
||||
{this.getToastText(toast.text)}
|
||||
</div>
|
||||
</div>
|
||||
{/* {toast.closable && <Cross className={classes["cross"]} onClick={this.onClose} />} */}
|
||||
</div>
|
||||
{toast.button}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public override componentDidMount() {
|
||||
if (this.props.toast.time === 0) return;
|
||||
|
||||
this.closeTimeout = window.setTimeout(() => {
|
||||
this.close();
|
||||
}, this.props.toast.time);
|
||||
}
|
||||
|
||||
private getToastTitle(title: string | React.ReactNode) {
|
||||
if (typeof title === "string") {
|
||||
return (
|
||||
<Typography typo={ITypo.P_16}>
|
||||
{title}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
private getToastText(text: React.ReactNode) {
|
||||
if (typeof text === "string") {
|
||||
return (
|
||||
<div className={classes["text-container"]}>
|
||||
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.GREY}>
|
||||
{text}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private onClose(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.close();
|
||||
}
|
||||
|
||||
private close() {
|
||||
window.clearTimeout(this.closeTimeout);
|
||||
this.setState({
|
||||
willClose: true,
|
||||
});
|
||||
setTimeout(() => {
|
||||
Toasts.getInstance().close(this.props.toast);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
import React from "react";
|
||||
|
||||
// Components
|
||||
|
||||
// Stores
|
||||
|
||||
// Styles
|
||||
import classes from "./classes.module.scss";
|
||||
import Toasts, { IToast } from "@Front/Stores/Toasts";
|
||||
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
|
||||
type IProps = {
|
||||
toast: IToast;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
willClose: boolean;
|
||||
};
|
||||
|
||||
export default class ToastElement extends React.Component<IProps, IState> {
|
||||
private closeTimeout = 0;
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
willClose: false,
|
||||
};
|
||||
|
||||
this.onClose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
const toast = this.props.toast;
|
||||
const style = {
|
||||
"--data-duration": `${toast.time}ms`,
|
||||
} as React.CSSProperties;
|
||||
return (
|
||||
<div className={classes["root"]} data-will-close={this.state.willClose}>
|
||||
{toast.time !== 0 && <div className={classes["loadbar"]} style={style} />}
|
||||
<div className={classes["header"]}>
|
||||
<div className={classes["text-icon_row"]}>
|
||||
{toast.icon && toast.icon}
|
||||
<div className={classes["text-container"]}>
|
||||
{this.getToastTitle(toast.title)}
|
||||
{this.getToastText(toast.text)}
|
||||
</div>
|
||||
</div>
|
||||
{/* {toast.closable && <Cross className={classes["cross"]} onClick={this.onClose} />} */}
|
||||
</div>
|
||||
{toast.button}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public override componentDidMount() {
|
||||
if (this.props.toast.time === 0) return;
|
||||
|
||||
this.closeTimeout = window.setTimeout(() => {
|
||||
this.close();
|
||||
}, this.props.toast.time);
|
||||
}
|
||||
|
||||
private getToastTitle(title: string | React.ReactNode) {
|
||||
if (typeof title === "string") {
|
||||
return <Typography typo={ITypo.P_16}>{title}</Typography>;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
private getToastText(text: React.ReactNode) {
|
||||
if (typeof text === "string") {
|
||||
return (
|
||||
<div className={classes["text-container"]}>
|
||||
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.GREY}>
|
||||
{text}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private onClose(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.close();
|
||||
}
|
||||
|
||||
private close() {
|
||||
window.clearTimeout(this.closeTimeout);
|
||||
this.setState({
|
||||
willClose: true,
|
||||
});
|
||||
setTimeout(() => {
|
||||
Toasts.getInstance().close(this.props.toast);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
& > *:not(:first-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 444px) {
|
||||
width: unset;
|
||||
left: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
& > *:not(:first-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 444px) {
|
||||
width: unset;
|
||||
left: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +1,46 @@
|
||||
import React from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import ToastElement from "./ToastElement";
|
||||
import classNames from "classnames";
|
||||
import Toasts, { IToast } from "@Front/Stores/Toasts";
|
||||
|
||||
type IProps = {};
|
||||
type IState = {
|
||||
toastList: IToast[];
|
||||
};
|
||||
export default class ToastsContainer extends React.Component<IProps, IState> {
|
||||
private removeOnChange = () => { };
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
toastList: Toasts.getInstance().toasts,
|
||||
};
|
||||
|
||||
this.updateToasts = this.updateToasts.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className={classNames(classes["root"], this.state.toastList.length > 0 && classes["open"])}>
|
||||
<>
|
||||
{this.state.toastList.map((toast) => {
|
||||
return <ToastElement toast={toast} key={toast.id} />;
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public override componentDidMount() {
|
||||
this.removeOnChange = Toasts.getInstance().onChange(this.updateToasts);
|
||||
}
|
||||
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnChange();
|
||||
}
|
||||
|
||||
private updateToasts(toastList: IToast[]) {
|
||||
this.setState({ toastList });
|
||||
}
|
||||
}
|
||||
import React from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import ToastElement from "./ToastElement";
|
||||
import classNames from "classnames";
|
||||
import Toasts, { IToast } from "@Front/Stores/Toasts";
|
||||
|
||||
type IProps = {};
|
||||
type IState = {
|
||||
toastList: IToast[];
|
||||
};
|
||||
export default class ToastsContainer extends React.Component<IProps, IState> {
|
||||
private removeOnChange = () => {};
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
toastList: Toasts.getInstance().toasts,
|
||||
};
|
||||
|
||||
this.updateToasts = this.updateToasts.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className={classNames(classes["root"], this.state.toastList.length > 0 && classes["open"])}>
|
||||
<>
|
||||
{this.state.toastList.map((toast) => {
|
||||
return <ToastElement toast={toast} key={toast.id} />;
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public override componentDidMount() {
|
||||
this.removeOnChange = Toasts.getInstance().onChange(this.updateToasts);
|
||||
}
|
||||
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnChange();
|
||||
}
|
||||
|
||||
private updateToasts(toastList: IToast[]) {
|
||||
this.setState({ toastList });
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ type IState = {};
|
||||
|
||||
export default class ToastHandler extends React.Component<IProps, IState> {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<ToastsContainer />
|
||||
);
|
||||
return <ToastsContainer />;
|
||||
}
|
||||
}
|
||||
|
@ -30,4 +30,4 @@
|
||||
right: 20px;
|
||||
margin-top: -4px;
|
||||
fill: $white;
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,12 @@ export default class TooltipContent extends React.Component<IProps> {
|
||||
public override render(): JSX.Element {
|
||||
const mousePositionX = this.props.event?.clientX;
|
||||
const mousePositionY = this.props.event?.clientY;
|
||||
let right = 0;
|
||||
let bottom = 0;
|
||||
if (typeof window !== "undefined") {
|
||||
right = window.innerWidth - (mousePositionX ?? 0) - 35;
|
||||
bottom = window.innerHeight - (mousePositionY ?? 0) + 15;
|
||||
}
|
||||
let right = 0;
|
||||
let bottom = 0;
|
||||
if (typeof window !== "undefined") {
|
||||
right = window.innerWidth - (mousePositionX ?? 0) - 35;
|
||||
bottom = window.innerHeight - (mousePositionY ?? 0) + 15;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={{ bottom: bottom + "px", right: right + "px" }}
|
||||
@ -39,4 +39,4 @@ export default class TooltipContent extends React.Component<IProps> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,13 +40,13 @@ export default class Tooltip extends React.Component<IProps, IState> {
|
||||
onMouseEnter={this.togglePopup}
|
||||
onMouseLeave={this.togglePopup}
|
||||
onMouseMove={this.moovePopup}>
|
||||
<Image src={ToolTipIcon} alt="toolTip icon"/>
|
||||
<Image src={ToolTipIcon} alt="toolTip icon" />
|
||||
</span>
|
||||
<Typography typo={ITypo.CAPTION_14}>
|
||||
<TooltipContent title={this.props.title} event={this.state.event} display={this.state.showContent}>
|
||||
{this.props.text}
|
||||
</TooltipContent>
|
||||
</Typography>
|
||||
<Typography typo={ITypo.CAPTION_14}>
|
||||
<TooltipContent title={this.props.title} event={this.state.event} display={this.state.showContent}>
|
||||
{this.props.text}
|
||||
</TooltipContent>
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -2,126 +2,126 @@
|
||||
@import "@Themes/modes.scss";
|
||||
|
||||
.root {
|
||||
color: $black;
|
||||
font-family: 'Inter', sans-serif;
|
||||
&.H1-60 {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 50px;
|
||||
line-height: 61px;
|
||||
}
|
||||
color: $black;
|
||||
font-family: "Inter", sans-serif;
|
||||
&.H1-60 {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 50px;
|
||||
line-height: 61px;
|
||||
}
|
||||
|
||||
&.H1-bis-40 {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 40px;
|
||||
line-height: 48px;
|
||||
}
|
||||
&.H1-bis-40 {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 40px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
&.H2-30 {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
line-height: 36px;
|
||||
}
|
||||
&.H2-30 {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
&.H3-24 {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 29px;
|
||||
}
|
||||
&.H3-24 {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 29px;
|
||||
}
|
||||
|
||||
&.Paragraphe-semibold-18 {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
&.Paragraphe-semibold-18 {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.Paragraphe-simple-18 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
}
|
||||
&.Paragraphe-simple-18 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
&.Paragraphe-MAJ-18 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
&.Paragraphe-MAJ-18 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&.Nav-header-off-18 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
&.Nav-header-off-18 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.Paragraphe-18-error {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
&.Paragraphe-18-error {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.Paragraphe-semibold-16 {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
&.Paragraphe-semibold-16 {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.Nav-input-off-16 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
color: $grey;
|
||||
}
|
||||
&.Nav-input-off-16 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
color: $grey;
|
||||
}
|
||||
|
||||
&.Paragraphe-simple-16 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.005em
|
||||
}
|
||||
&.Paragraphe-simple-16 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.005em;
|
||||
}
|
||||
|
||||
&.Paragraphe-16-error {
|
||||
color: $re-hover;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
&.Paragraphe-16-error {
|
||||
color: $re-hover;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.Caption_14 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
&.Caption_14 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.re-hover {
|
||||
color: $re-hover;
|
||||
}
|
||||
&.re-hover {
|
||||
color: $re-hover;
|
||||
}
|
||||
|
||||
&.grey {
|
||||
color: $grey;
|
||||
}
|
||||
&.grey {
|
||||
color: $grey;
|
||||
}
|
||||
|
||||
&.black {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
&.black {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
@ -3,50 +3,44 @@ import classes from "./classes.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
||||
type IProps = {
|
||||
typo: ITypo;
|
||||
children: React.ReactNode;
|
||||
color?: ITypoColor;
|
||||
typo: ITypo;
|
||||
children: React.ReactNode;
|
||||
color?: ITypoColor;
|
||||
};
|
||||
type IState = {};
|
||||
|
||||
export enum ITypo {
|
||||
H1 = "H1-60",
|
||||
H1Bis = "H1-bis-40",
|
||||
H2 = "H2-30",
|
||||
H3 = "H3-24",
|
||||
H1 = "H1-60",
|
||||
H1Bis = "H1-bis-40",
|
||||
H2 = "H2-30",
|
||||
H3 = "H3-24",
|
||||
|
||||
P_SB_18 = "Paragraphe-semibold-18",
|
||||
P_18 = "Paragraphe-simple-18",
|
||||
P_MAJ_18 = "Paragraphe-MAJ-18",
|
||||
NAV_HEADER_18 = "Nav-header-off-18",
|
||||
P_ERR_18 = "Paragraphe-18-error",
|
||||
P_SB_18 = "Paragraphe-semibold-18",
|
||||
P_18 = "Paragraphe-simple-18",
|
||||
P_MAJ_18 = "Paragraphe-MAJ-18",
|
||||
NAV_HEADER_18 = "Nav-header-off-18",
|
||||
P_ERR_18 = "Paragraphe-18-error",
|
||||
|
||||
P_SB_16 = "Paragraphe-semibold-16",
|
||||
P_16 = "Paragraphe-simple-16",
|
||||
NAV_INPUT_16 = "Nav-input-off-16",
|
||||
P_ERR_16 = "Paragraphe-16-error",
|
||||
P_SB_16 = "Paragraphe-semibold-16",
|
||||
P_16 = "Paragraphe-simple-16",
|
||||
NAV_INPUT_16 = "Nav-input-off-16",
|
||||
P_ERR_16 = "Paragraphe-16-error",
|
||||
|
||||
CAPTION_14 = "Caption_14",
|
||||
CAPTION_14 = "Caption_14",
|
||||
}
|
||||
|
||||
export enum ITypoColor {
|
||||
RE_HOVER = "re-hover",
|
||||
GREY = "grey",
|
||||
BLACK = 'black'
|
||||
RE_HOVER = "re-hover",
|
||||
GREY = "grey",
|
||||
BLACK = "black",
|
||||
}
|
||||
|
||||
export default class Typography extends React.Component<IProps, IState> {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
classes["root"],
|
||||
classes[this.props.typo],
|
||||
classes[this.props.color ?? ""]
|
||||
)}
|
||||
>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className={classNames(classes["root"], classes[this.props.typo], classes[this.props.color ?? ""])}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
right: 24px;
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
right: 24px;
|
||||
}
|
||||
|
@ -7,13 +7,13 @@ type IProps = {};
|
||||
type IState = {};
|
||||
|
||||
export default class Version extends React.Component<IProps, IState> {
|
||||
public override render(): JSX.Element {
|
||||
return <div className={classes["root"]}>
|
||||
<Typography typo={ITypo.P_16} color={ITypoColor.GREY}>
|
||||
<div>
|
||||
{VersionFile.version}
|
||||
</div>
|
||||
</Typography>
|
||||
</div>;
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className={classes["root"]}>
|
||||
<Typography typo={ITypo.P_16} color={ITypoColor.GREY}>
|
||||
<div>{VersionFile.version}</div>
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ type IProps = {};
|
||||
type IState = {};
|
||||
|
||||
export default class _Template extends React.Component<IProps, IState> {
|
||||
public override render(): JSX.Element {
|
||||
return <div className={classes["root"]}></div>;
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return <div className={classes["root"]}></div>;
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,39 @@
|
||||
@keyframes BurgerTopForward {
|
||||
0% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
0% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(-8.000023662749896px, 2.000005915687474px, 0) rotate(-45.000133102968164deg) scaleX(0.4999985210781315)
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(-8.000023662749896px, 2.000005915687474px, 0) rotate(-45.000133102968164deg) scaleX(0.4999985210781315);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes BurgerTopBackward {
|
||||
0% {
|
||||
transform: translate3d(-8.000023662749896px, 2.000005915687474px, 0) rotate(-45.000133102968164deg) scaleX(0.4999985210781315)
|
||||
}
|
||||
0% {
|
||||
transform: translate3d(-8.000023662749896px, 2.000005915687474px, 0) rotate(-45.000133102968164deg) scaleX(0.4999985210781315);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes BurgerBottomForward {
|
||||
0% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
0% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(-8.000023662749896px, -2.000005915687474px, 0) rotate(45.000133102968164deg) scaleX(0.4999985210781315);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(-8.000023662749896px, -2.000005915687474px, 0) rotate(45.000133102968164deg) scaleX(0.4999985210781315);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes BurgerBottomBackward {
|
||||
0% {
|
||||
transform: translate3d(-8.000023662749896px, -2.000005915687474px, 0) rotate(45.000133102968164deg) scaleX(0.4999985210781315);
|
||||
}
|
||||
0% {
|
||||
transform: translate3d(-8.000023662749896px, -2.000005915687474px, 0) rotate(45.000133102968164deg) scaleX(0.4999985210781315);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(0px, 0px, 0) rotate(0deg) scaleX(1);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
@import "@Themes/constants.scss";
|
||||
@import "./animation.scss";
|
||||
|
||||
|
||||
.root {
|
||||
|
||||
position: fixed;
|
||||
left: 18px;
|
||||
top: 23px;
|
||||
@ -30,18 +28,16 @@
|
||||
background-color: $textColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.inner-top {
|
||||
top: 0;
|
||||
|
||||
&.true {
|
||||
animation: BurgerTopForward 0.2s cubic-bezier(.43,1.89,.68,.28);
|
||||
animation: BurgerTopForward 0.2s cubic-bezier(0.43, 1.89, 0.68, 0.28);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.false {
|
||||
animation: BurgerTopBackward 0.2s cubic-bezier(.43,1.89,.68,.28);
|
||||
animation: BurgerTopBackward 0.2s cubic-bezier(0.43, 1.89, 0.68, 0.28);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
@ -59,14 +55,13 @@
|
||||
bottom: 1px;
|
||||
|
||||
&.true {
|
||||
animation: BurgerBottomForward 0.2s cubic-bezier(.43,1.89,.68,.28);
|
||||
animation: BurgerBottomForward 0.2s cubic-bezier(0.43, 1.89, 0.68, 0.28);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.false {
|
||||
animation: BurgerBottomBackward 0.2s cubic-bezier(.43,1.89,.68,.28);
|
||||
animation: BurgerBottomBackward 0.2s cubic-bezier(0.43, 1.89, 0.68, 0.28);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ import classes from "./classes.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
||||
type IProps = {
|
||||
state: boolean,
|
||||
callback: () => void
|
||||
state: boolean;
|
||||
callback: () => void;
|
||||
};
|
||||
|
||||
export default function Burger({ state, callback }: IProps) {
|
||||
return (
|
||||
<div className={classes["root"]} onClick={() => callback()}>
|
||||
<div className={classes["box"]} >
|
||||
<div className={classes["box"]}>
|
||||
<div className={classNames(classes["inner-top"], classes["inner"], classes[String(state)])} />
|
||||
<div className={classNames(classes["inner-middle"], classes["inner"], classes[String(state)])} />
|
||||
<div className={classNames(classes["inner-bottom"], classes["inner"], classes[String(state)])} />
|
||||
|
@ -2,122 +2,121 @@
|
||||
@import "@Themes/animation.scss";
|
||||
|
||||
.root {
|
||||
padding: 15px 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
will-change: transform, opacity;
|
||||
animation: fadeInFromLeft 500ms;
|
||||
padding: 15px 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
will-change: transform, opacity;
|
||||
animation: fadeInFromLeft 500ms;
|
||||
|
||||
>h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
> h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.component {
|
||||
width: 100%;
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 40px;
|
||||
padding: 12px 16px 12px 40px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: $borderColor;
|
||||
border-radius: 4px;
|
||||
transition: border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
|
||||
will-change: border-color, box-shadow;
|
||||
background-color: $backgroundColor;
|
||||
.component {
|
||||
width: 100%;
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 40px;
|
||||
padding: 12px 16px 12px 40px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: $borderColor;
|
||||
border-radius: 4px;
|
||||
transition: border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
|
||||
will-change: border-color, box-shadow;
|
||||
background-color: $backgroundColor;
|
||||
|
||||
&:hover {
|
||||
border-color: $primaryColor;
|
||||
}
|
||||
&:hover {
|
||||
border-color: $primaryColor;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px $primaryColor;
|
||||
border-color: $primaryColor;
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px $primaryColor;
|
||||
border-color: $primaryColor;
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-color: $downColor;
|
||||
&.error {
|
||||
border-color: $downColor;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px rgba(237, 29, 37, 0.1);
|
||||
}
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px rgba(237, 29, 37, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&.success {
|
||||
border-color: $upColor;
|
||||
&.success {
|
||||
border-color: $upColor;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px rgba(0, 201, 167, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px rgba(0, 201, 167, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoomIn {
|
||||
from {
|
||||
transform: scale(.2);
|
||||
opacity: 0
|
||||
}
|
||||
@keyframes zoomIn {
|
||||
from {
|
||||
transform: scale(0.2);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
visibility: visible;
|
||||
pointer-events: none;
|
||||
will-change: transform, opacity;
|
||||
.status {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
visibility: visible;
|
||||
pointer-events: none;
|
||||
will-change: transform, opacity;
|
||||
|
||||
&.error {
|
||||
animation: zoomIn 0.3s cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||
}
|
||||
&.error {
|
||||
animation: zoomIn 0.3s cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||
}
|
||||
|
||||
&.success {
|
||||
animation: zoomIn 0.3s cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
z-index: 1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
visibility: visible;
|
||||
pointer-events: none;
|
||||
}
|
||||
&.success {
|
||||
animation: zoomIn 0.3s cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
z-index: 1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
visibility: visible;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.errorMsg {
|
||||
color: $downColor;
|
||||
margin: -15px 0 15px 0;
|
||||
line-height: 24px;
|
||||
will-change: transform, opacity;
|
||||
animation: slideDown 0.3s cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
color: $downColor;
|
||||
margin: -15px 0 15px 0;
|
||||
line-height: 24px;
|
||||
will-change: transform, opacity;
|
||||
animation: slideDown 0.3s cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@ -1,50 +1,50 @@
|
||||
import React, { RefObject } from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import classNames from "classnames"
|
||||
import Image from "next/image"
|
||||
import ErrorIcon from "@Assets/icons/input-error.svg"
|
||||
import SuccessIcon from "@Assets/icons/input-success.svg"
|
||||
import classNames from "classnames";
|
||||
import Image from "next/image";
|
||||
import ErrorIcon from "@Assets/icons/input-error.svg";
|
||||
import SuccessIcon from "@Assets/icons/input-success.svg";
|
||||
|
||||
type IProps = {
|
||||
inputRef?: RefObject<HTMLInputElement>,
|
||||
icon?: string,
|
||||
placeholder?: string,
|
||||
name?: string,
|
||||
value?: string,
|
||||
onChange: any,
|
||||
onBlur?: any,
|
||||
inputStatus: 'success' | 'error' | 'neutral',
|
||||
errorMsg?: string
|
||||
type: string
|
||||
inputRef?: RefObject<HTMLInputElement>;
|
||||
icon?: string;
|
||||
placeholder?: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
onChange: any;
|
||||
onBlur?: any;
|
||||
inputStatus: "success" | "error" | "neutral";
|
||||
errorMsg?: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export default class InputField extends React.Component<IProps> {
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return <>
|
||||
<div className={classNames(classes["root"])}>
|
||||
{this.props.icon && <Image className={classes["icon"]} alt={this.props.icon} src={this.props.icon} />}
|
||||
<input className={classNames(classes["component"], classes[this.props.inputStatus])}
|
||||
ref={this.props.inputRef}
|
||||
type={this.props.type}
|
||||
name={this.props.name}
|
||||
placeholder={this.props.placeholder}
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
onBlur={this.props.onBlur}
|
||||
autoComplete={this.props.name}
|
||||
/>
|
||||
<div className={classNames(classes["status"], classes[this.props.inputStatus])}>
|
||||
{this.props.inputStatus === "success" &&
|
||||
<Image alt="success icon" src={SuccessIcon} />}
|
||||
{this.props.inputStatus === "error" &&
|
||||
<Image alt="error icon" src={ErrorIcon} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{this.props.errorMsg && <div className={classes["errorMsg"]}>{this.props.errorMsg}</div>}
|
||||
</>
|
||||
}
|
||||
}
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div className={classNames(classes["root"])}>
|
||||
{this.props.icon && <Image className={classes["icon"]} alt={this.props.icon} src={this.props.icon} />}
|
||||
<input
|
||||
className={classNames(classes["component"], classes[this.props.inputStatus])}
|
||||
ref={this.props.inputRef}
|
||||
type={this.props.type}
|
||||
name={this.props.name}
|
||||
placeholder={this.props.placeholder}
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
onBlur={this.props.onBlur}
|
||||
autoComplete={this.props.name}
|
||||
/>
|
||||
<div className={classNames(classes["status"], classes[this.props.inputStatus])}>
|
||||
{this.props.inputStatus === "success" && <Image alt="success icon" src={SuccessIcon} />}
|
||||
{this.props.inputStatus === "error" && <Image alt="error icon" src={ErrorIcon} />}
|
||||
</div>
|
||||
</div>
|
||||
{this.props.errorMsg && <div className={classes["errorMsg"]}>{this.props.errorMsg}</div>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
import Head from 'next/head';
|
||||
import { ReactNode } from 'react';
|
||||
import Head from "next/head";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type DefaultLayoutProps = { children: ReactNode };
|
||||
|
||||
export const DefaultLayout = ({ children }: DefaultLayoutProps) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>LECoffre</title>
|
||||
<link rel="shortcut icon" href="/favicon-16x16.png" />
|
||||
</Head>
|
||||
<main>{children}</main>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>LECoffre</title>
|
||||
<link rel="shortcut icon" href="/favicon-16x16.png" />
|
||||
</Head>
|
||||
<main>{children}</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -27,7 +27,6 @@ export default class DefaultTemplate extends React.Component<IProps, IState> {
|
||||
</div>
|
||||
<Version />
|
||||
</>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@ -37,4 +36,4 @@ export default class DefaultTemplate extends React.Component<IProps, IState> {
|
||||
window.scrollTo(0, this.props.scrollTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ import React from "react";
|
||||
type _IProps = {};
|
||||
type _IState = {};
|
||||
|
||||
export default class BasePage<IProps = _IProps, IState = _IState> extends React.Component<IProps, IState> {}
|
||||
export default class BasePage<IProps = _IProps, IState = _IState> extends React.Component<IProps, IState> {}
|
||||
|
@ -1,14 +1,14 @@
|
||||
.root {
|
||||
margin-left: 35px;
|
||||
margin-right: 35px;
|
||||
.section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.sub-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
margin-left: 35px;
|
||||
margin-right: 35px;
|
||||
.section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.sub-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.inline-flex{
|
||||
display: inline-flex;
|
||||
}
|
||||
.inline-flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
.root {
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ import BasePage from "../Base";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
export default class Home extends BasePage {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<DefaultTemplate title={"HomePage"}>
|
||||
<div className={classes["root"]}>
|
||||
<Typography typo={ITypo.H1}>HomePage</Typography>
|
||||
</div>
|
||||
</DefaultTemplate>
|
||||
);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<DefaultTemplate title={"HomePage"}>
|
||||
<div className={classes["root"]}>
|
||||
<Typography typo={ITypo.H1}>HomePage</Typography>
|
||||
</div>
|
||||
</DefaultTemplate>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,20 @@
|
||||
@import "@Themes/animation.scss";
|
||||
|
||||
.root {
|
||||
margin: 100px auto 20px auto;
|
||||
font-size: 4em;
|
||||
text-align: center;
|
||||
will-change: opacity, transform;
|
||||
animation: fadeInFromTop 500ms;
|
||||
margin: 100px auto 20px auto;
|
||||
font-size: 4em;
|
||||
text-align: center;
|
||||
will-change: opacity, transform;
|
||||
animation: fadeInFromTop 500ms;
|
||||
|
||||
.text {
|
||||
margin: 20px auto 20px auto;
|
||||
font-size: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
.text {
|
||||
margin: 20px auto 20px auto;
|
||||
font-size: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.home-button {
|
||||
margin: 50px auto;
|
||||
max-width: 150px;
|
||||
}
|
||||
}
|
||||
.home-button {
|
||||
margin: 50px auto;
|
||||
max-width: 150px;
|
||||
}
|
||||
}
|
||||
|
@ -4,30 +4,21 @@ import BasePage from "../Base";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
export default class PageNotFound extends BasePage {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<DefaultTemplate title={"Project Not Found"}>
|
||||
<div className={classes["root"]}>
|
||||
{/* <Image alt="Unplugged" height="50" src={UnpluggedIcon} /> Oops */}
|
||||
<div className={classes["text"]}>There isn't anything here...</div>
|
||||
<div className={classes["home-button"]}>
|
||||
<Link href="/">
|
||||
{/* <Button text="Go to Home" icon={CardsIcon} /> */}
|
||||
</Link>
|
||||
</div>
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<DefaultTemplate title={"Project Not Found"}>
|
||||
<div className={classes["root"]}>
|
||||
{/* <Image alt="Unplugged" height="50" src={UnpluggedIcon} /> Oops */}
|
||||
<div className={classes["text"]}>There isn't anything here...</div>
|
||||
<div className={classes["home-button"]}>
|
||||
<Link href="/">{/* <Button text="Go to Home" icon={CardsIcon} /> */}</Link>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
<input />
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</DefaultTemplate>
|
||||
);
|
||||
}
|
||||
<label>
|
||||
<input />
|
||||
</label>
|
||||
</div>
|
||||
</DefaultTemplate>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -8,51 +8,45 @@ import Burger from "@Front/Components/Elements/Burger";
|
||||
|
||||
type IProps = {};
|
||||
type IState = {
|
||||
status: boolean;
|
||||
status: boolean;
|
||||
};
|
||||
|
||||
export default class Header extends React.Component<IProps, IState> {
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
status: false,
|
||||
};
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
status: false,
|
||||
};
|
||||
|
||||
this.switchStatus = this.switchStatus.bind(this);
|
||||
}
|
||||
this.switchStatus = this.switchStatus.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div className={classes["root"]}>
|
||||
<Burger state={this.state.status} callback={this.switchStatus} />
|
||||
<div className={classes["menu"]}>
|
||||
<Link href="/">
|
||||
<Image alt="TEZOS LINK" src={TezosLinkLogo} />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes["triangle-under-logo"]}></div>
|
||||
<div className={classes["logo"]}>
|
||||
<Link href="/">
|
||||
<Image alt="entire stack" src={Logo} />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes["button"]}>
|
||||
<Link href="/show-project">
|
||||
{/* <Button color="transparent" text="MY PROJECT" icon={LoginIcon} /> */}
|
||||
</Link>
|
||||
<Link href="/new-project">
|
||||
{/* <Button text="CREATE" icon={PlusCardIcon} /> */}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div className={classes["root"]}>
|
||||
<Burger state={this.state.status} callback={this.switchStatus} />
|
||||
<div className={classes["menu"]}>
|
||||
<Link href="/">
|
||||
<Image alt="TEZOS LINK" src={TezosLinkLogo} />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes["triangle-under-logo"]}></div>
|
||||
<div className={classes["logo"]}>
|
||||
<Link href="/">
|
||||
<Image alt="entire stack" src={Logo} />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes["button"]}>
|
||||
<Link href="/show-project">{/* <Button color="transparent" text="MY PROJECT" icon={LoginIcon} /> */}</Link>
|
||||
<Link href="/new-project">{/* <Button text="CREATE" icon={PlusCardIcon} /> */}</Link>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private switchStatus() {
|
||||
this.state.status
|
||||
? this.setState({ status: false })
|
||||
: this.setState({ status: true });
|
||||
}
|
||||
private switchStatus() {
|
||||
this.state.status ? this.setState({ status: false }) : this.setState({ status: true });
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import EventEmitter from "@Front/Services/EventEmitter";
|
||||
|
||||
|
||||
export default abstract class BaseStore {
|
||||
protected readonly event = new EventEmitter();
|
||||
|
||||
|
@ -1,106 +1,106 @@
|
||||
import EventEmitter from "@Front/Services/EventEmitter";
|
||||
// import I18n from "Components/Elements/I18n";
|
||||
|
||||
export enum EToastPriority {
|
||||
HIGH = "high",
|
||||
LOW = "low",
|
||||
}
|
||||
|
||||
export interface IToast {
|
||||
id?: number;
|
||||
title: string | React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
text?: string | React.ReactNode;
|
||||
button?: React.ReactNode;
|
||||
time?: number;
|
||||
closable?: boolean;
|
||||
priority?: EToastPriority;
|
||||
}
|
||||
|
||||
export default class Toasts {
|
||||
private static ctx: Toasts;
|
||||
private readonly event = new EventEmitter();
|
||||
private toastList: IToast[] = [];
|
||||
private uid: number = 0;
|
||||
|
||||
private defaultTime: IToast["time"] = 10000;
|
||||
private defaultClosable: IToast["closable"] = true;
|
||||
private defaultPriority: IToast["priority"] = EToastPriority.LOW;
|
||||
|
||||
private constructor() {
|
||||
Toasts.ctx = this;
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!Toasts.ctx) new this();
|
||||
return Toasts.ctx;
|
||||
}
|
||||
|
||||
public get toasts() {
|
||||
return this.toastList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns removelistener callback
|
||||
*/
|
||||
public onChange(callback: (toastList: IToast[]) => void) {
|
||||
this.event.on("change", callback);
|
||||
return () => this.event.off("change", callback);
|
||||
}
|
||||
|
||||
public open(toast: IToast): () => void {
|
||||
const index = this.toastList.indexOf(toast);
|
||||
if (index !== -1) return () => this.close(toast);
|
||||
|
||||
toast.id = toast.id ?? this.uid++;
|
||||
toast.time = toast.time ?? this.defaultTime;
|
||||
toast.closable = toast.closable ?? this.defaultClosable;
|
||||
toast.priority = toast.priority ?? this.defaultPriority;
|
||||
|
||||
const highToasts = this.toastList.filter((toast) => {
|
||||
return toast.priority === EToastPriority.HIGH;
|
||||
});
|
||||
|
||||
const lowToasts = this.toastList.filter((toast) => {
|
||||
return toast.priority === EToastPriority.LOW;
|
||||
});
|
||||
|
||||
if (toast.priority === EToastPriority.HIGH) {
|
||||
highToasts.unshift(toast);
|
||||
} else {
|
||||
lowToasts.unshift(toast);
|
||||
}
|
||||
|
||||
this.toastList.splice(0);
|
||||
this.toastList.unshift(...lowToasts);
|
||||
this.toastList.unshift(...highToasts);
|
||||
|
||||
this.event.emit("change", this.toastList);
|
||||
|
||||
return () => this.close(toast);
|
||||
}
|
||||
|
||||
public close(toast: IToast) {
|
||||
const index = this.toastList.indexOf(toast);
|
||||
if (index === -1) return;
|
||||
this.toastList.splice(index, 1);
|
||||
this.event.emit("change", this.toastList);
|
||||
}
|
||||
|
||||
/**
|
||||
* An utility static method you can use to quickly display an error toast
|
||||
* with a custom error message.
|
||||
*
|
||||
* @param message_key a key to a l18n message.
|
||||
*/
|
||||
public static errorToast(message_key: string) {
|
||||
Toasts.getInstance().open({
|
||||
text: "Toast text",
|
||||
title: "Toast title",
|
||||
// text: <I18n map={message_key} />,
|
||||
// title: <I18n map="toast.an_error_occured" />,
|
||||
closable: true,
|
||||
priority: EToastPriority.HIGH,
|
||||
});
|
||||
}
|
||||
}
|
||||
import EventEmitter from "@Front/Services/EventEmitter";
|
||||
// import I18n from "Components/Elements/I18n";
|
||||
|
||||
export enum EToastPriority {
|
||||
HIGH = "high",
|
||||
LOW = "low",
|
||||
}
|
||||
|
||||
export interface IToast {
|
||||
id?: number;
|
||||
title: string | React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
text?: string | React.ReactNode;
|
||||
button?: React.ReactNode;
|
||||
time?: number;
|
||||
closable?: boolean;
|
||||
priority?: EToastPriority;
|
||||
}
|
||||
|
||||
export default class Toasts {
|
||||
private static ctx: Toasts;
|
||||
private readonly event = new EventEmitter();
|
||||
private toastList: IToast[] = [];
|
||||
private uid: number = 0;
|
||||
|
||||
private defaultTime: IToast["time"] = 10000;
|
||||
private defaultClosable: IToast["closable"] = true;
|
||||
private defaultPriority: IToast["priority"] = EToastPriority.LOW;
|
||||
|
||||
private constructor() {
|
||||
Toasts.ctx = this;
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!Toasts.ctx) new this();
|
||||
return Toasts.ctx;
|
||||
}
|
||||
|
||||
public get toasts() {
|
||||
return this.toastList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns removelistener callback
|
||||
*/
|
||||
public onChange(callback: (toastList: IToast[]) => void) {
|
||||
this.event.on("change", callback);
|
||||
return () => this.event.off("change", callback);
|
||||
}
|
||||
|
||||
public open(toast: IToast): () => void {
|
||||
const index = this.toastList.indexOf(toast);
|
||||
if (index !== -1) return () => this.close(toast);
|
||||
|
||||
toast.id = toast.id ?? this.uid++;
|
||||
toast.time = toast.time ?? this.defaultTime;
|
||||
toast.closable = toast.closable ?? this.defaultClosable;
|
||||
toast.priority = toast.priority ?? this.defaultPriority;
|
||||
|
||||
const highToasts = this.toastList.filter((toast) => {
|
||||
return toast.priority === EToastPriority.HIGH;
|
||||
});
|
||||
|
||||
const lowToasts = this.toastList.filter((toast) => {
|
||||
return toast.priority === EToastPriority.LOW;
|
||||
});
|
||||
|
||||
if (toast.priority === EToastPriority.HIGH) {
|
||||
highToasts.unshift(toast);
|
||||
} else {
|
||||
lowToasts.unshift(toast);
|
||||
}
|
||||
|
||||
this.toastList.splice(0);
|
||||
this.toastList.unshift(...lowToasts);
|
||||
this.toastList.unshift(...highToasts);
|
||||
|
||||
this.event.emit("change", this.toastList);
|
||||
|
||||
return () => this.close(toast);
|
||||
}
|
||||
|
||||
public close(toast: IToast) {
|
||||
const index = this.toastList.indexOf(toast);
|
||||
if (index === -1) return;
|
||||
this.toastList.splice(index, 1);
|
||||
this.event.emit("change", this.toastList);
|
||||
}
|
||||
|
||||
/**
|
||||
* An utility static method you can use to quickly display an error toast
|
||||
* with a custom error message.
|
||||
*
|
||||
* @param message_key a key to a l18n message.
|
||||
*/
|
||||
public static errorToast(message_key: string) {
|
||||
Toasts.getInstance().open({
|
||||
text: "Toast text",
|
||||
title: "Toast title",
|
||||
// text: <I18n map={message_key} />,
|
||||
// title: <I18n map="toast.an_error_occured" />,
|
||||
closable: true,
|
||||
priority: EToastPriority.HIGH,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
# Documentation
|
||||
|
||||
This is the documentation served at `https://<BASE_PATH>/documentation`
|
||||
This is the documentation served at `https://<BASE_PATH>/documentation`
|
||||
|
@ -5,18 +5,21 @@ Once sign up, a `Project ID` is generated for your project, to use within your a
|
||||
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>');
|
||||
>
|
||||
> ```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>|
|
||||
| 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
|
||||
|
||||
@ -37,26 +40,29 @@ The `Project ID` authorize requests.
|
||||
|
||||
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`
|
||||
> 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)
|
||||
[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)
|
||||
|
||||
|
@ -4,4 +4,3 @@
|
||||
- [Security](##security)
|
||||
- [RPC Endpoints](#rpc-endpoints)
|
||||
- [Nodes](#nodes)
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user