mvp progress
12
ui/src/core/assets/icons/add.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<g id="SVGRepo_iconCarrier"> <path d="M12 7.75732L12 16.2426" stroke="#f46036" stroke-width="2" stroke-linecap="round"/> <path d="M7.75735 12L16.2426 12" stroke="#f46036" stroke-width="2" stroke-linecap="round"/> <rect x="3" y="3" width="18" height="18" rx="3" stroke="#f46036" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 769 B |
7
ui/src/core/assets/icons/check.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="SVGRepo_iconCarrier"> <g id="Interface / Check"> <path id="Vector" d="M6 12L10.2426 16.2426L18.727 7.75732" stroke="#f8f9fa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g> </g>
|
||||
</svg>
|
After Width: | Height: | Size: 628 B |
7
ui/src/core/assets/icons/chevron.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="SVGRepo_iconCarrier"> <path d="M15.8351 11.6296L9.20467 5.1999C8.79094 4.79869 8 5.04189 8 5.5703L8 18.4297C8 18.9581 8.79094 19.2013 9.20467 18.8001L15.8351 12.3704C16.055 12.1573 16.0549 11.8427 15.8351 11.6296Z" fill="#013a63"/> </g>
|
||||
</svg>
|
After Width: | Height: | Size: 664 B |
13
ui/src/core/assets/icons/delete.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<rect width="24" height="24" fill="none"/> <path d="M7 17L16.8995 7.10051" stroke="#f55d3e" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 7.00001L16.8995 16.8995" stroke="#f46036" stroke-linecap="round" stroke-linejoin="round"/> </g>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 701 B |
1
ui/src/core/assets/icons/error.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 64 64" id="error"><circle cx="32" cy="32" r="28" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></circle><line x1="32" x2="32" y1="18" y2="38" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></line><line x1="32" x2="32" y1="42" y2="46" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></line></svg>
|
After Width: | Height: | Size: 434 B |
3
ui/src/core/assets/icons/left_icon.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7071 4.29289C12.0976 4.68342 12.0976 5.31658 11.7071 5.70711L6.41421 11H20C20.5523 11 21 11.4477 21 12C21 12.5523 20.5523 13 20 13H6.41421L11.7071 18.2929C12.0976 18.6834 12.0976 19.3166 11.7071 19.7071C11.3166 20.0976 10.6834 20.0976 10.2929 19.7071L3.29289 12.7071C3.10536 12.5196 3 12.2652 3 12C3 11.7348 3.10536 11.4804 3.29289 11.2929L10.2929 4.29289C10.6834 3.90237 11.3166 3.90237 11.7071 4.29289Z" fill="#000000"/>
|
||||
</svg>
|
After Width: | Height: | Size: 584 B |
3
ui/src/core/assets/icons/reload.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7071 1.29289C14.0976 1.68342 14.0976 2.31658 13.7071 2.70711L12.4053 4.00896C17.1877 4.22089 21 8.16524 21 13C21 17.9706 16.9706 22 12 22C7.02944 22 3 17.9706 3 13C3 12.4477 3.44772 12 4 12C4.55228 12 5 12.4477 5 13C5 16.866 8.13401 20 12 20C15.866 20 19 16.866 19 13C19 9.2774 16.0942 6.23349 12.427 6.01281L13.7071 7.29289C14.0976 7.68342 14.0976 8.31658 13.7071 8.70711C13.3166 9.09763 12.6834 9.09763 12.2929 8.70711L9.29289 5.70711C9.10536 5.51957 9 5.26522 9 5C9 4.73478 9.10536 4.48043 9.29289 4.29289L12.2929 1.29289C12.6834 0.902369 13.3166 0.902369 13.7071 1.29289Z" fill="#0F1729"/>
|
||||
</svg>
|
After Width: | Height: | Size: 755 B |
12
ui/src/core/assets/images/logo.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M2 5.75C2 5.33579 2.33579 5 2.75 5H20.75C21.1642 5 21.5 5.33579 21.5 5.75C21.5 6.16421 21.1642 6.5 20.75 6.5H2.75C2.33579 6.5 2 6.16421 2 5.75ZM2 9.75C2 9.33579 2.33579 9 2.75 9H20.75C21.1642 9 21.5 9.33579 21.5 9.75C21.5 10.1642 21.1642 10.5 20.75 10.5H2.75C2.33579 10.5 2 10.1642 2 9.75ZM20.2113 12.6586C20.5379 12.9134 20.5961 13.3847 20.3414 13.7113L16.4414 18.7113C16.3022 18.8897 16.0899 18.9958 15.8636 18.9999C15.6373 19.004 15.4213 18.9057 15.2757 18.7324L13.1757 16.2324C12.9093 15.9152 12.9504 15.4421 13.2676 15.1757C13.5848 14.9093 14.0579 14.9504 14.3243 15.2676L15.8284 17.0582L19.1586 12.7887C19.4134 12.4621 19.8847 12.4039 20.2113 12.6586ZM2 13.75C2 13.3358 2.33579 13 2.75 13H9.75C10.1642 13 10.5 13.3358 10.5 13.75C10.5 14.1642 10.1642 14.5 9.75 14.5H2.75C2.33579 14.5 2 14.1642 2 13.75ZM2 17.75C2 17.3358 2.33579 17 2.75 17H9.75C10.1642 17 10.5 17.3358 10.5 17.75C10.5 18.1642 10.1642 18.5 9.75 18.5H2.75C2.33579 18.5 2 18.1642 2 17.75Z" fill="#f46036"/> </g>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
45
ui/src/core/extensions/array.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
export {};
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
// @strict: The parameter is determined whether the arrays must be exactly the same in content and order of this relationship or simply follow the same requirements.
|
||||
equals(array: Array<T>, strict: boolean): boolean;
|
||||
lastElement(): T | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export const ArrayExtensions = () => {
|
||||
if ([].equals === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.equals = function (array, strict = true) {
|
||||
if (!array) return false;
|
||||
|
||||
if (arguments.length === 1) strict = true;
|
||||
|
||||
if (this.length !== array.length) return false;
|
||||
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (this[i] instanceof Array && array[i] instanceof Array) {
|
||||
if (!this[i].equals(array[i], strict)) return false;
|
||||
} else if (strict && this[i] !== array[i]) {
|
||||
return false;
|
||||
} else if (!strict) {
|
||||
return this.sort().equals(array.sort(), true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
if ([].lastElement === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.lastElement = function () {
|
||||
let instanceCheck = this;
|
||||
if (instanceCheck === undefined) {
|
||||
return undefined;
|
||||
} else {
|
||||
let instance = instanceCheck as [];
|
||||
return instance[instance.length - 1];
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
6
ui/src/core/extensions/extensions.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { ArrayExtensions } from "./array";
|
||||
|
||||
export const extensions = () =>{
|
||||
ArrayExtensions()
|
||||
}
|
||||
|
535
ui/src/core/helper/result.ts
Normal file
|
@ -0,0 +1,535 @@
|
|||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
|
||||
function isAsyncFn(fn: Function) {
|
||||
return fn.constructor.name === "AsyncFunction";
|
||||
}
|
||||
|
||||
function isResult(value: unknown): value is Result<any, any, any> {
|
||||
return value instanceof Ok || value instanceof Err;
|
||||
}
|
||||
|
||||
interface SyncThenable {
|
||||
isSync: true;
|
||||
then<Fn extends () => Promise<any>>(cb: Fn): ReturnType<Fn>;
|
||||
then<Fn extends () => any>(cb: Fn): SyncThenable;
|
||||
}
|
||||
|
||||
|
||||
function syncThenable(): SyncThenable {
|
||||
function then<Fn extends () => Promise<any>>(cb: Fn): ReturnType<Fn>;
|
||||
function then<Fn extends () => any>(cb: Fn): SyncThenable;
|
||||
function then(cb: any) {
|
||||
const result = cb();
|
||||
if (result instanceof Promise) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return syncThenable();
|
||||
}
|
||||
|
||||
return {
|
||||
isSync: true,
|
||||
then,
|
||||
};
|
||||
}
|
||||
|
||||
function forEachValueThunkOrPromise<T>(
|
||||
items: unknown[],
|
||||
execFn: (value: T) => boolean,
|
||||
foldFn: () => unknown
|
||||
) {
|
||||
let shouldBreak = false;
|
||||
|
||||
const result: any = items.reduce((prev: { then: Function }, valueOrThunk) => {
|
||||
return prev.then(() => {
|
||||
if (shouldBreak) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function run(value: T) {
|
||||
const isSuccess = execFn(value);
|
||||
if (!isSuccess) {
|
||||
shouldBreak = true;
|
||||
}
|
||||
}
|
||||
|
||||
const valueOrPromise =
|
||||
typeof valueOrThunk === "function" ? valueOrThunk() : valueOrThunk;
|
||||
|
||||
if (valueOrPromise instanceof Promise) {
|
||||
return valueOrPromise.then(run);
|
||||
}
|
||||
|
||||
return run(valueOrPromise);
|
||||
});
|
||||
}, syncThenable());
|
||||
|
||||
if ((result as SyncThenable).isSync) {
|
||||
return foldFn();
|
||||
}
|
||||
|
||||
return result.then(() => {
|
||||
return foldFn();
|
||||
});
|
||||
}
|
||||
|
||||
export type Result<
|
||||
ErrorType,
|
||||
OkType,
|
||||
RollbackFn extends RollbackFunction = any
|
||||
> = Ok<ErrorType, OkType, RollbackFn> | Err<ErrorType, OkType, RollbackFn>;
|
||||
|
||||
interface IResult<ErrorType, OkType> {
|
||||
isSuccess(): this is Ok<ErrorType, OkType, any>;
|
||||
|
||||
isFailure(): this is Err<ErrorType, OkType, any>;
|
||||
|
||||
getOrNull(): OkType | null;
|
||||
|
||||
toString(): string;
|
||||
|
||||
inspect(): string;
|
||||
|
||||
fold<R>(
|
||||
onSuccess: (value: OkType) => R,
|
||||
onFailure: (error: ErrorType) => R
|
||||
): R;
|
||||
fold<R>(
|
||||
onSuccess: (value: OkType) => Promise<R>,
|
||||
onFailure: (error: ErrorType) => Promise<R>
|
||||
): Promise<R>;
|
||||
|
||||
getOrDefault(defaultValue: OkType): OkType;
|
||||
|
||||
getOrElse(onFailure: (error: ErrorType) => OkType): OkType;
|
||||
getOrElse(onFailure: (error: ErrorType) => Promise<OkType>): Promise<OkType>;
|
||||
|
||||
getOrThrow(): OkType;
|
||||
|
||||
map<T>(
|
||||
fn: (value: OkType) => Promise<T>
|
||||
): Promise<
|
||||
JoinErrorTypes<
|
||||
ErrorType,
|
||||
T extends Result<any, any, any> ? T : Result<Error, T, any>
|
||||
>
|
||||
>;
|
||||
map<T>(
|
||||
fn: (value: OkType) => T
|
||||
): JoinErrorTypes<
|
||||
ErrorType,
|
||||
T extends Result<any, any, any> ? T : Result<Error, T, any>
|
||||
>;
|
||||
|
||||
rollback(): Result<Error, void> | Promise<Result<Error, void>>;
|
||||
}
|
||||
|
||||
type InferErrorType<T extends Result<any, any, any>> = T extends Result<
|
||||
infer Errortype,
|
||||
any,
|
||||
any
|
||||
>
|
||||
? Errortype
|
||||
: never;
|
||||
|
||||
type InferOkType<T extends Result<any, any, any>> = T extends Result<
|
||||
any,
|
||||
infer OkType,
|
||||
any
|
||||
>
|
||||
? OkType
|
||||
: never;
|
||||
|
||||
type JoinErrorTypes<ErrorType, B extends Result<any, any, any>> = Result<
|
||||
ErrorType | InferErrorType<B>,
|
||||
InferOkType<B>,
|
||||
any
|
||||
>;
|
||||
|
||||
type ExtractErrorTypes<Tuple extends any[]> = {
|
||||
[Index in keyof Tuple]: Tuple[Index] extends Result<any, any, any>
|
||||
? InferErrorType<Tuple[Index]>
|
||||
: never;
|
||||
}[number];
|
||||
|
||||
type MapResultTupleToOkTypeTuple<Tuple extends any[]> = {
|
||||
[Index in keyof Tuple]: Tuple[Index] extends Result<any, any, any>
|
||||
? InferOkType<Tuple[Index]>
|
||||
: never;
|
||||
};
|
||||
|
||||
type RollbackFunction = (() => void) | (() => Promise<void>);
|
||||
|
||||
type HasAsyncRollbackFunction<T extends any[]> = {
|
||||
[Index in keyof T]: T[Index] extends () => Promise<infer U> | infer U
|
||||
? U extends Result<any, any, () => Promise<void>>
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
}[number] extends false
|
||||
? false
|
||||
: true;
|
||||
|
||||
type UnwrapThunks<T extends any[]> = {
|
||||
[Index in keyof T]: T[Index] extends () => Promise<infer U>
|
||||
? U
|
||||
: T[Index] extends () => infer U
|
||||
? U
|
||||
: T[Index];
|
||||
};
|
||||
|
||||
type HasAsyncThunk<T extends any[]> = {
|
||||
[Index in keyof T]: T[Index] extends () => Promise<any> ? true : false;
|
||||
}[number] extends false
|
||||
? false
|
||||
: true;
|
||||
|
||||
type PromiseReturnType<T extends (...args: any) => any> = T extends (
|
||||
...args: any
|
||||
) => Promise<infer U>
|
||||
? U
|
||||
: never;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||
export namespace Result {
|
||||
export function ok<
|
||||
ErrorType extends unknown,
|
||||
OkType,
|
||||
RollbackFn extends RollbackFunction = any
|
||||
>(
|
||||
value?: OkType,
|
||||
rollbackFn?: RollbackFn
|
||||
): Result<ErrorType, OkType, RollbackFn> {
|
||||
return new Ok<ErrorType, OkType, RollbackFn>(
|
||||
value || null!,
|
||||
rollbackFn
|
||||
) as any;
|
||||
}
|
||||
|
||||
export function error<
|
||||
ErrorType extends unknown,
|
||||
OkType extends unknown,
|
||||
RollbackFn extends RollbackFunction = any
|
||||
>(
|
||||
error: ErrorType,
|
||||
rollbackFn?: RollbackFn
|
||||
): Result<ErrorType, OkType, RollbackFn> {
|
||||
return new Err<ErrorType, OkType, RollbackFn>(error, rollbackFn);
|
||||
}
|
||||
|
||||
type SafeReturnType<E, T> = T extends Result<any, any, any>
|
||||
? Result<E | InferErrorType<T>, InferOkType<T>, never>
|
||||
: Result<E, T, never>;
|
||||
|
||||
export function safe<T>(
|
||||
fn: () => Promise<T>
|
||||
): Promise<SafeReturnType<Error, T>>;
|
||||
export function safe<T>(fn: () => T): SafeReturnType<Error, T>;
|
||||
export function safe<ErrorType, T>(
|
||||
err: ErrorType | (new (...args: any[]) => ErrorType),
|
||||
fn: () => Promise<T>
|
||||
): Promise<SafeReturnType<ErrorType, T>>;
|
||||
export function safe<ErrorType, T>(
|
||||
err: ErrorType | (new (...args: any[]) => ErrorType),
|
||||
fn: () => T
|
||||
): SafeReturnType<ErrorType, T>;
|
||||
export function safe(errOrFn: any, fn?: any) {
|
||||
const hasCustomError = fn !== undefined;
|
||||
|
||||
const execute = hasCustomError ? fn : errOrFn;
|
||||
|
||||
function getError(caughtError: Error) {
|
||||
if (!hasCustomError) {
|
||||
return caughtError;
|
||||
}
|
||||
|
||||
if (typeof errOrFn === "function") {
|
||||
return new errOrFn(caughtError);
|
||||
}
|
||||
|
||||
return errOrFn;
|
||||
}
|
||||
|
||||
try {
|
||||
const resultOrPromise = execute();
|
||||
|
||||
if (resultOrPromise instanceof Promise) {
|
||||
return resultOrPromise
|
||||
.then((okValue) => {
|
||||
return isResult(okValue) ? okValue : Result.ok(okValue);
|
||||
})
|
||||
.catch((caughtError) => error(getError(caughtError)));
|
||||
}
|
||||
|
||||
return isResult(resultOrPromise)
|
||||
? resultOrPromise
|
||||
: Result.ok(resultOrPromise);
|
||||
} catch (caughtError) {
|
||||
return error(getError(caughtError as Error));
|
||||
}
|
||||
}
|
||||
|
||||
type CombineResult<
|
||||
T extends (unknown | (() => unknown) | (() => Promise<unknown>))[]
|
||||
> = Result<
|
||||
ExtractErrorTypes<UnwrapThunks<T>>,
|
||||
MapResultTupleToOkTypeTuple<UnwrapThunks<T>>,
|
||||
HasAsyncRollbackFunction<T> extends true ? () => Promise<void> : () => void
|
||||
>;
|
||||
|
||||
export function combine<
|
||||
T extends (unknown | (() => unknown) | (() => Promise<unknown>))[]
|
||||
>(
|
||||
...items: T
|
||||
): HasAsyncThunk<T> extends true
|
||||
? Promise<CombineResult<T>>
|
||||
: CombineResult<T> {
|
||||
if (!items.length) {
|
||||
throw new Error("Expected at least 1 argument");
|
||||
}
|
||||
|
||||
const values: unknown[] = [];
|
||||
const rollbacks: RollbackFunction[] = [];
|
||||
let error: Err<unknown, unknown, any> | null = null;
|
||||
|
||||
function rollback() {
|
||||
const reversedRollbacks = rollbacks.reverse();
|
||||
const wrappedRollbackFns = reversedRollbacks.map((fn) => Result.wrap(fn));
|
||||
|
||||
let error: Err<unknown, unknown, any> | null = null;
|
||||
|
||||
return forEachValueThunkOrPromise(
|
||||
wrappedRollbackFns,
|
||||
(result: Result<unknown, unknown>) => {
|
||||
if (result.isFailure()) {
|
||||
error = Result.error<unknown, unknown, any>(result.error) as any;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
() => error || ok()
|
||||
);
|
||||
}
|
||||
|
||||
return forEachValueThunkOrPromise(
|
||||
items,
|
||||
(result: Result<unknown, unknown>) => {
|
||||
if (result.isFailure()) {
|
||||
error = Result.error<unknown, unknown>(result.error, rollback) as any;
|
||||
return false;
|
||||
}
|
||||
|
||||
values.push(result.value);
|
||||
rollbacks.push(() => result.rollback());
|
||||
return true;
|
||||
},
|
||||
() => error || ok(values, rollback)
|
||||
);
|
||||
}
|
||||
|
||||
export function wrap<Fn extends (...args: any) => Promise<any>>(
|
||||
fn: Fn
|
||||
): (
|
||||
...args: Parameters<Fn>
|
||||
) => Promise<Result<Error, PromiseReturnType<Fn>, never>>;
|
||||
export function wrap<Fn extends (...args: any) => any>(
|
||||
fn: Fn
|
||||
): (...args: Parameters<Fn>) => Result<Error, ReturnType<Fn>, never>;
|
||||
export function wrap(fn: any) {
|
||||
return function wrapped(...args: any) {
|
||||
try {
|
||||
const resultOrPromise = fn(...args);
|
||||
|
||||
if (resultOrPromise instanceof Promise) {
|
||||
return resultOrPromise
|
||||
.then((okValue) => Result.ok(okValue))
|
||||
.catch((err) => error(err));
|
||||
}
|
||||
|
||||
return ok(resultOrPromise);
|
||||
} catch (err) {
|
||||
return error(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Base<
|
||||
ErrorType extends unknown,
|
||||
OkType extends unknown,
|
||||
RollbackFn extends RollbackFunction
|
||||
> implements IResult<ErrorType, OkType>
|
||||
{
|
||||
constructor(protected readonly rollbackFn?: RollbackFn) {}
|
||||
|
||||
errorOrNull(): ErrorType | null {
|
||||
if (this.isSuccess()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (this as any).error as ErrorType;
|
||||
}
|
||||
|
||||
getOrNull(): OkType | null {
|
||||
if (this.isFailure()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (this as any).value as OkType;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
inspect(): string {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
fold<R>(
|
||||
onSuccess: (value: OkType) => R,
|
||||
onFailure: (error: ErrorType) => R
|
||||
): R;
|
||||
|
||||
fold<R>(
|
||||
onSuccess: (value: OkType) => Promise<R>,
|
||||
onFailure: (error: ErrorType) => Promise<R>
|
||||
): Promise<R>;
|
||||
fold(onSuccess: any, onFailure: any) {
|
||||
if (this.isFailure()) {
|
||||
return onFailure(this.error);
|
||||
}
|
||||
|
||||
return onSuccess((this as any).value as OkType);
|
||||
}
|
||||
|
||||
getOrDefault(defaultValue: OkType): OkType {
|
||||
if (this.isSuccess()) {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
getOrElse(onFailure: (error: ErrorType) => OkType): OkType;
|
||||
getOrElse(onFailure: (error: ErrorType) => Promise<OkType>): Promise<OkType>;
|
||||
getOrElse(onFailure: any) {
|
||||
if (this.isSuccess()) {
|
||||
return isAsyncFn(onFailure) ? Promise.resolve(this.value) : this.value;
|
||||
}
|
||||
|
||||
return onFailure((this as any).error as ErrorType);
|
||||
}
|
||||
|
||||
getOrThrow(): OkType {
|
||||
if (this.isFailure()) {
|
||||
throw this.error;
|
||||
}
|
||||
|
||||
return (this as any).value as OkType;
|
||||
}
|
||||
|
||||
isSuccess(): this is Ok<ErrorType, OkType, RollbackFn> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
isFailure(): this is Err<ErrorType, OkType, RollbackFn> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
map<T>(
|
||||
fn: (value: OkType) => Promise<T>
|
||||
): Promise<
|
||||
JoinErrorTypes<
|
||||
ErrorType,
|
||||
T extends Result<any, any, any> ? T : Result<Error, T, any>
|
||||
>
|
||||
>;
|
||||
map<T>(
|
||||
fn: (value: OkType) => T
|
||||
): JoinErrorTypes<
|
||||
ErrorType,
|
||||
T extends Result<any, any, any> ? T : Result<Error, T, any>
|
||||
>;
|
||||
map(fn: any) {
|
||||
if (this.isFailure()) {
|
||||
return isAsyncFn(fn) ? Promise.resolve(this) : this;
|
||||
}
|
||||
|
||||
const result = Result.safe(() => fn((this as any).value) as any);
|
||||
|
||||
return result as any;
|
||||
}
|
||||
|
||||
rollback(): RollbackFn extends RollbackFunction
|
||||
? RollbackFn extends () => Promise<void>
|
||||
? Promise<Result<Error, void>>
|
||||
: Result<Error, void>
|
||||
: void {
|
||||
if (this.rollbackFn) {
|
||||
return this.rollbackFn() as any;
|
||||
}
|
||||
|
||||
return null as any;
|
||||
}
|
||||
}
|
||||
|
||||
class Ok<
|
||||
ErrorType extends unknown,
|
||||
OkType extends unknown,
|
||||
RollbackFn extends RollbackFunction
|
||||
> extends Base<ErrorType, OkType, RollbackFn> {
|
||||
public readonly value: OkType;
|
||||
|
||||
constructor(val: OkType, rollbackFn?: RollbackFn) {
|
||||
super(rollbackFn);
|
||||
this.value = val;
|
||||
}
|
||||
|
||||
isSuccess(): this is Ok<ErrorType, OkType, RollbackFn> {
|
||||
return true;
|
||||
}
|
||||
|
||||
isFailure(): this is Err<ErrorType, OkType, RollbackFn> {
|
||||
return false;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `Result.Ok(${this.value})`;
|
||||
}
|
||||
|
||||
forward(): Result<any, OkType, RollbackFn> {
|
||||
return Result.ok(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
class Err<
|
||||
ErrorType extends unknown,
|
||||
OkType extends unknown,
|
||||
RollbackFn extends RollbackFunction
|
||||
> extends Base<ErrorType, OkType, RollbackFn> {
|
||||
public readonly error: ErrorType;
|
||||
|
||||
constructor(err: ErrorType, rollbackFn?: RollbackFn) {
|
||||
super(rollbackFn);
|
||||
this.error = err;
|
||||
}
|
||||
|
||||
isSuccess(): this is Ok<ErrorType, OkType, RollbackFn> {
|
||||
return false;
|
||||
}
|
||||
|
||||
isFailure(): this is Err<ErrorType, OkType, RollbackFn> {
|
||||
return true;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `Result.Error(${this.error})`;
|
||||
}
|
||||
|
||||
forward(): Result<ErrorType, any, RollbackFn> {
|
||||
return Result.error(this.error);
|
||||
}
|
||||
}
|
3
ui/src/core/helper/validate.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function validateRequired(value: string) {
|
||||
return value ? undefined : "required";
|
||||
}
|
15
ui/src/core/l10n/i18n.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import { resources } from "./locale";
|
||||
i18n
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
resources,
|
||||
lng: "en", // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
|
||||
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
|
||||
// if you're using a language detector, do not define the lng option
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // react already safes from xss
|
||||
},
|
||||
});
|
12
ui/src/core/l10n/locale.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export const resources = {
|
||||
en: {
|
||||
translation: {
|
||||
"Welcome to React": "Welcome to React and react-i18next",
|
||||
},
|
||||
},
|
||||
fr: {
|
||||
translation: {
|
||||
"Welcome to React": "Bienvenue à React et react-i18next",
|
||||
},
|
||||
},
|
||||
};
|
11
ui/src/core/model/trigger_model.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export interface ITriggerModel {
|
||||
_id?: string;
|
||||
type: string;
|
||||
description:string;
|
||||
value: string[];
|
||||
}
|
||||
|
||||
export enum TriggerType {
|
||||
PROCESS = "PROCESS",
|
||||
FILE = "FILE",
|
||||
}
|
|
@ -5,7 +5,7 @@ export enum HttpMethod {
|
|||
|
||||
export class HttpRepository {
|
||||
|
||||
private server = 'http://localhost:3000'
|
||||
private server = 'http://localhost:4001'
|
||||
|
||||
public async jsonRequest<T>(method: HttpMethod, url: string, data?: any): Promise<T> {
|
||||
const reqInit = {
|
||||
|
|
14
ui/src/core/repository/socket_repository.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Socket, io } from "socket.io-client";
|
||||
|
||||
export class SocketRepository {
|
||||
serverURL = "ws://localhost:4001";
|
||||
socket: Socket | undefined;
|
||||
async connect() {
|
||||
const socket = io(this.serverURL);
|
||||
this.socket = socket;
|
||||
socket.connect();
|
||||
socket.on('mock', (d) =>{
|
||||
console.log(d)
|
||||
})
|
||||
}
|
||||
}
|
36
ui/src/core/routers/routers.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { createBrowserRouter } from "react-router-dom";
|
||||
import {
|
||||
AllProjectScreen,
|
||||
AllProjectScreenPath,
|
||||
} from "../../features/all_projects/presentation/all_projects_screen";
|
||||
import {
|
||||
PipelineInstanceScreen,
|
||||
PipelineScreenPath,
|
||||
} from "../../features/pipeline_instance_main_screen/pipeline_instance_screen";
|
||||
import {
|
||||
SelectProjectScreen,
|
||||
SelectProjectScreenPath,
|
||||
} from "../../features/select_project/presentation/select_project";
|
||||
import {
|
||||
CreatePipelineScreen,
|
||||
CreatePipelineScreenPath,
|
||||
} from "../../features/create_pipeline/presentation/create_pipeline_screen";
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: AllProjectScreenPath,
|
||||
element: <AllProjectScreen />,
|
||||
},
|
||||
{
|
||||
path: PipelineScreenPath,
|
||||
element: <PipelineInstanceScreen />,
|
||||
},
|
||||
{
|
||||
path: SelectProjectScreenPath,
|
||||
element: <SelectProjectScreen />,
|
||||
},
|
||||
{
|
||||
path: CreatePipelineScreenPath,
|
||||
element: <CreatePipelineScreen />,
|
||||
},
|
||||
]);
|
94
ui/src/core/ui/header/header.tsx
Normal file
|
@ -0,0 +1,94 @@
|
|||
import * as React from "react";
|
||||
import { Typography } from "antd";
|
||||
import { Col, Row } from "antd";
|
||||
import { LinkTypography } from "../link/link";
|
||||
import { ReactComponent as LeftIcon } from "../../assets/icons/left_icon.svg";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export interface IHeader {
|
||||
largeText: string;
|
||||
minText?: string;
|
||||
path?: string;
|
||||
needBackButton?: undefined | any;
|
||||
}
|
||||
|
||||
export const Header: React.FunctionComponent<IHeader> = (props: IHeader) => {
|
||||
const navigate = useNavigate();
|
||||
const needBackButton = props.needBackButton !== undefined ? false : true;
|
||||
|
||||
return (
|
||||
<Col style={{ textAlign: "center" }}>
|
||||
<Row
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
marginRight: "20px",
|
||||
display: "contents",
|
||||
}}
|
||||
>
|
||||
{needBackButton ? (
|
||||
<>
|
||||
<div
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
left: "10px",
|
||||
backgroundColor: "#456BD9",
|
||||
border: "0.1875em solid #0F1C3F",
|
||||
borderRadius: "50%",
|
||||
height: "60px",
|
||||
width: "60px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<LeftIcon
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
|
||||
cursor: "pointer",
|
||||
width: "40px",
|
||||
height: "40px",
|
||||
position: "relative",
|
||||
bottom: "-8px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Title
|
||||
style={{
|
||||
position: "relative",
|
||||
bottom: "-8px",
|
||||
left: "20px",
|
||||
width: "100vw",
|
||||
}}
|
||||
level={2}
|
||||
>
|
||||
{props.largeText}
|
||||
</Title>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Title style={{ justifyContent: "center" }} level={2}>
|
||||
{props.largeText}
|
||||
</Title>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
{props.minText !== undefined ? (
|
||||
<LinkTypography
|
||||
style={{
|
||||
marginBottom: "40px",
|
||||
}}
|
||||
path={props.path!}
|
||||
text={props.minText}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
};
|
28
ui/src/core/ui/link/link.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import * as React from "react";
|
||||
import { Typography } from "antd";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const { Link } = Typography;
|
||||
|
||||
export interface ILinkTypography {
|
||||
path: string;
|
||||
text: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export const LinkTypography: React.FunctionComponent<ILinkTypography> = (
|
||||
props: ILinkTypography
|
||||
) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Link
|
||||
style={props.style}
|
||||
onClick={() => {
|
||||
navigate(props.path);
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</Link>
|
||||
);
|
||||
};
|
66
ui/src/core/ui/list/list.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { Row } from "antd";
|
||||
import { ReactComponent as AddIcon } from "../../assets/icons/add.svg";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
export type CallBackFunction = (a: string) => void;
|
||||
|
||||
export interface ListElement {
|
||||
text: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export interface IPropsList {
|
||||
values: ListElement[];
|
||||
headers?: string;
|
||||
onClick?: CallBackFunction;
|
||||
}
|
||||
|
||||
export const List: React.FunctionComponent<IPropsList> = observer(
|
||||
(props) => {
|
||||
return (
|
||||
<div>
|
||||
{props.headers !== undefined ? <>{props.headers}</> : <></>}
|
||||
{props.values.map((el) => {
|
||||
return (
|
||||
<Row
|
||||
style={{
|
||||
width: "300px",
|
||||
backgroundColor: el.color ?? "ActiveBorder",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "-webkit-fill-available",
|
||||
margin: "5px",
|
||||
marginLeft: "40px",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "40px",
|
||||
}}
|
||||
>
|
||||
{el.text}
|
||||
</div>
|
||||
<div style={{ flexGrow: "1" }}></div>
|
||||
<AddIcon
|
||||
style={{
|
||||
width: "50px",
|
||||
cursor: "pointer",
|
||||
height: "50px",
|
||||
marginRight: "40px",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (props.onClick !== undefined) {
|
||||
props.onClick(el.text);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
31
ui/src/core/ui/loader/loader.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
.loader {
|
||||
position: relative;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
transform: rotate(45deg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.loader:after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 8px;
|
||||
margin: auto;
|
||||
background: #222b32;
|
||||
}
|
||||
.loader:before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -15px;
|
||||
margin: auto;
|
||||
background: #de3500;
|
||||
animation: diamondLoader 2s linear infinite;
|
||||
}
|
||||
@keyframes diamondLoader {
|
||||
0% ,10% {
|
||||
transform: translate(-64px , -64px) rotate(-45deg)
|
||||
}
|
||||
90% , 100% {
|
||||
transform: translate(0px , 0px) rotate(-45deg)
|
||||
}
|
||||
}
|
16
ui/src/core/ui/loader/loader.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as React from "react";
|
||||
import "./loader.css";
|
||||
|
||||
export const Loader: React.FunctionComponent = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
placeItems: "center",
|
||||
}}
|
||||
>
|
||||
<div className="loader" />
|
||||
</div>
|
||||
);
|
||||
};
|
49
ui/src/core/ui/pages/load_page.tsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
import * as React from "react";
|
||||
import { Header, IHeader } from "../header/header";
|
||||
import { Loader } from "../loader/loader";
|
||||
import { ReactComponent as ErrorIcon } from "../../assets/icons/error.svg";
|
||||
import { Typography } from "antd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
const { Title } = Typography;
|
||||
|
||||
interface ILoadPage extends IHeader {
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
children?: JSX.Element | JSX.Element[];
|
||||
}
|
||||
|
||||
export const LoadPage: React.FunctionComponent<ILoadPage> = observer((
|
||||
props: ILoadPage
|
||||
) => {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
path={props.path}
|
||||
largeText={props.largeText}
|
||||
minText={props.minText}
|
||||
/>
|
||||
{props.isError ? (
|
||||
<>
|
||||
<ErrorIcon
|
||||
style={{
|
||||
height: "100px",
|
||||
width: "-webkit-fill-available",
|
||||
}}
|
||||
/>
|
||||
<Title style={{ textAlign: "center" }} level={3} type="danger">
|
||||
not expected error
|
||||
</Title>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{props.isLoading ? (
|
||||
<div style={{'marginTop':'50px'}}>
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>{props.children}</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|