Merge branch 'feature/add-button-component' of http://85.143.176.51:3000/free-land/front-end into feature/add-button-component
This commit is contained in:
commit
e6c1944832
@ -1,9 +1,68 @@
|
|||||||
export const parameters = {
|
import "../src/index.css";
|
||||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
/**
|
||||||
controls: {
|
* Read https://storybook.js.org/docs/react/configure/overview#configure-story-rendering
|
||||||
matchers: {
|
* for more information about the purpose of this file.
|
||||||
color: /(background|color)$/i,
|
*
|
||||||
date: /Date$/,
|
* Use preview.js for global code (such as CSS imports or JavaScript mocks)
|
||||||
},
|
* that applies to all stories. For example, `import thirdPartyCss.css`.
|
||||||
},
|
*
|
||||||
}
|
* This file can have three exports:
|
||||||
|
* - decorators - an array of global decorators
|
||||||
|
* - parameters - an object of global parameters
|
||||||
|
* - globalTypes - definition of globalTypes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorators
|
||||||
|
*
|
||||||
|
* A decorator is a way to wrap a story in extra “rendering” functionality.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* import React from 'react';
|
||||||
|
* export const decorators = [(Story) => <div style={{ margin: '3em' }}><Story/></div>];
|
||||||
|
*
|
||||||
|
* Each story throughout the library will be wrapped in a div with a margin of 3
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters
|
||||||
|
*
|
||||||
|
* Most Storybook addons are configured via a parameter-based API.
|
||||||
|
* You can set global parameters in this file
|
||||||
|
*
|
||||||
|
* export const parameters = {
|
||||||
|
* backgrounds: {
|
||||||
|
* values: [
|
||||||
|
* { name: 'red', value: '#f00' },
|
||||||
|
* { name: 'green', value: '#0f0' },
|
||||||
|
* ],
|
||||||
|
* },
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* With backgrounds, you configure the list of backgrounds that every story can render in.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global Types
|
||||||
|
*
|
||||||
|
* Global Types allow you to add your own toolbars by creating
|
||||||
|
* globalTypes with a toolbar annotation:
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* export const globalTypes = {
|
||||||
|
* theme: {
|
||||||
|
* name: 'Theme',
|
||||||
|
* description: 'Global theme for components',
|
||||||
|
* defaultValue: 'light',
|
||||||
|
* toolbar: {
|
||||||
|
* icon: 'circlehollow',
|
||||||
|
* // array of plain string values or MenuItem shape
|
||||||
|
* items: ['light', 'dark'],
|
||||||
|
* },
|
||||||
|
* },
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Will add a new dropdown in your toolbar with options light and dark.
|
||||||
|
**/
|
||||||
|
17259
package-lock.json
generated
17259
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
59
package.json
59
package.json
@ -1,32 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "test",
|
"name": "freeland",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.5.0",
|
"@headlessui/react": "^1.6.6",
|
||||||
"@react-keycloak/web": "^3.4.0",
|
"@reduxjs/toolkit": "^1.8.3",
|
||||||
"@reduxjs/toolkit": "^1.8.1",
|
"@types/node": "^16.11.47",
|
||||||
"axios": "^0.26.1",
|
"@types/react": "^18.0.15",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"axios": "^0.27.2",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"graphql": "^16.4.0",
|
"graphql": "^16.5.0",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
"i18next": "^21.6.16",
|
"i18next": "^21.8.16",
|
||||||
"i18next-browser-languagedetector": "^6.1.4",
|
"i18next-browser-languagedetector": "^6.1.4",
|
||||||
"i18next-http-backend": "^1.4.0",
|
"i18next-http-backend": "^1.4.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"keycloak-js": "^18.0.0",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash.debounce": "^4.0.8",
|
"react": "^18.2.0",
|
||||||
"react": "^18.0.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dom": "^18.0.0",
|
|
||||||
"react-hotkeys": "^2.0.0",
|
"react-hotkeys": "^2.0.0",
|
||||||
"react-i18next": "^11.16.7",
|
"react-i18next": "^11.18.3",
|
||||||
"react-redux": "^7.2.8",
|
"react-redux": "^8.0.2",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
"react-scripts": "5.0.0",
|
"react-scripts": "5.0.1",
|
||||||
"react-scrollbars-custom": "^4.0.27",
|
"react-scrollbars-custom": "^4.1.0",
|
||||||
|
"typescript": "^4.7.4",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
@ -45,6 +45,14 @@
|
|||||||
"react-app/jest"
|
"react-app/jest"
|
||||||
],
|
],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"**/*.stories.*"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"import/no-anonymous-default-export": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"**/*.stories.*"
|
"**/*.stories.*"
|
||||||
@ -68,6 +76,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/preset-typescript": "^7.18.6",
|
||||||
"@storybook/addon-actions": "^6.5.9",
|
"@storybook/addon-actions": "^6.5.9",
|
||||||
"@storybook/addon-essentials": "^6.5.9",
|
"@storybook/addon-essentials": "^6.5.9",
|
||||||
"@storybook/addon-interactions": "^6.5.9",
|
"@storybook/addon-interactions": "^6.5.9",
|
||||||
@ -79,22 +88,16 @@
|
|||||||
"@storybook/react": "^6.5.9",
|
"@storybook/react": "^6.5.9",
|
||||||
"@storybook/testing-library": "^0.0.13",
|
"@storybook/testing-library": "^0.0.13",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^12.1.4",
|
"@testing-library/react": "^13.3.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/lodash.debounce": "^4.0.6",
|
"autoprefixer": "^10.4.8",
|
||||||
"@types/node": "^16.11.26",
|
|
||||||
"@types/react": "^17.0.44",
|
|
||||||
"@types/react-dom": "^17.0.15",
|
|
||||||
"@types/react-redux": "^7.1.23",
|
|
||||||
"autoprefixer": "^10.4.4",
|
|
||||||
"babel-plugin-named-exports-order": "^0.0.2",
|
"babel-plugin-named-exports-order": "^0.0.2",
|
||||||
"jest": "^28.1.3",
|
"jest": "^28.1.3",
|
||||||
"postcss": "^8.4.12",
|
"postcss": "^8.4.14",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react-test-renderer": "^18.2.0",
|
"react-test-renderer": "^18.2.0",
|
||||||
"tailwindcss": "^3.0.23",
|
"tailwindcss": "^3.1.7",
|
||||||
"typescript": "^4.6.3",
|
|
||||||
"webpack": "^5.74.0"
|
"webpack": "^5.74.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
src/assets/svg/edit1.svg
Normal file
3
src/assets/svg/edit1.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 15L3.5 16.5L2 22L7.5 20.5L9 19M5 15L17 3C17.2626 2.73735 17.5744 2.52901 17.9176 2.38687C18.2608 2.24473 18.6286 2.17157 19 2.17157C19.3714 2.17157 19.7392 2.24473 20.0824 2.38687C20.4256 2.52901 20.7374 2.73735 21 3C21.2626 3.26264 21.471 3.57444 21.6131 3.9176C21.7553 4.26077 21.8284 4.62856 21.8284 5C21.8284 5.37143 21.7553 5.73923 21.6131 6.08239C21.471 6.42555 21.2626 6.73735 21 7L9 19M5 15L9 19" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 563 B |
4
src/assets/svg/edit2.svg
Normal file
4
src/assets/svg/edit2.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11 4H4C3.46957 4 2.96086 4.21071 2.58579 4.58579C2.21071 4.96086 2 5.46957 2 6V19C2 19.5304 2.21071 20.0391 2.58579 20.4142C2.96086 20.7893 3.46957 21 4 21H18C18.5304 21 19.0391 20.7893 19.4142 20.4142C19.7893 20.0391 20 19.5304 20 19V13" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M18.5 2.49998C18.8978 2.10216 19.4374 1.87866 20 1.87866C20.5626 1.87866 21.1022 2.10216 21.5 2.49998C21.8978 2.89781 22.1213 3.43737 22.1213 3.99998C22.1213 4.56259 21.8978 5.10216 21.5 5.49998L12 15L8 16L9 12L18.5 2.49998Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 697 B |
4
src/assets/svg/eye.svg
Normal file
4
src/assets/svg/eye.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 12C1 12 5 4 12 4C19 4 23 12 23 12C23 12 19 20 12 20C5 20 1 12 1 12Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 417 B |
58
src/components/Checkbox.stories.tsx
Normal file
58
src/components/Checkbox.stories.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||||
|
|
||||||
|
import Checkbox from './Checkbox';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Title inside navigation bar
|
||||||
|
title: 'Checkbox',
|
||||||
|
// Component to test
|
||||||
|
component: Checkbox,
|
||||||
|
// Clarifying the way how to process specific
|
||||||
|
// properties of your component and which values
|
||||||
|
// it can accept.
|
||||||
|
argTypes: {
|
||||||
|
checked: {
|
||||||
|
options: [true, false],
|
||||||
|
control: { type: 'radio' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ComponentMeta<typeof Checkbox>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a way to define a tempalte for your component.
|
||||||
|
*
|
||||||
|
* This template should cover all the states.
|
||||||
|
*
|
||||||
|
* In most cases you should just distruct args attribute
|
||||||
|
* on a returning component.
|
||||||
|
*/
|
||||||
|
const Template: ComponentStory<typeof Checkbox> = (args) => (
|
||||||
|
<Checkbox {...args} />
|
||||||
|
);
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* States of your component */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
export const Unchecked = Template.bind({});
|
||||||
|
Unchecked.args = {
|
||||||
|
label: "On/off lights",
|
||||||
|
checked: false,
|
||||||
|
children: undefined,
|
||||||
|
}
|
||||||
|
export const Checked = Template.bind({});
|
||||||
|
Checked.args = {
|
||||||
|
children: "On/off lights",
|
||||||
|
checked: true,
|
||||||
|
label: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EitherLabelChildren = Template.bind({});
|
||||||
|
EitherLabelChildren.args = {
|
||||||
|
children: "On/off lights",
|
||||||
|
checked: true,
|
||||||
|
label: "Label!",
|
||||||
|
}
|
||||||
|
// GO Further
|
39
src/components/Checkbox.tsx
Normal file
39
src/components/Checkbox.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
/**
|
||||||
|
* The way to provide `children` inside a component
|
||||||
|
* via attribute instead of enclosed tag
|
||||||
|
*/
|
||||||
|
label?: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* When you will have both `children` and `label`
|
||||||
|
* defined on a component it will choose `children`
|
||||||
|
* to display
|
||||||
|
*/
|
||||||
|
children?: React.ReactNode;
|
||||||
|
} & Omit<React.ComponentPropsWithoutRef<"input">, "type">;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customized `input[type="checkbox"]` component with label
|
||||||
|
*
|
||||||
|
* All common input properties are inherited.
|
||||||
|
*
|
||||||
|
* To define a label content either provide `label` attribute
|
||||||
|
* or use common `children` property
|
||||||
|
*/
|
||||||
|
const Checkbox = ({label, children, className, ...props}: Props) => {
|
||||||
|
|
||||||
|
if(label && !children) {
|
||||||
|
children = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label className={classNames(className, "flex items-center")}>
|
||||||
|
<input type="checkbox" {...props} />
|
||||||
|
{children !== undefined && <span>{children}</span>}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Checkbox;
|
44
src/components/Container.stories.tsx
Normal file
44
src/components/Container.stories.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||||
|
|
||||||
|
import Container from './Container';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Title inside navigation bar
|
||||||
|
title: 'Container',
|
||||||
|
// Component to test
|
||||||
|
component: Container,
|
||||||
|
// Clarifying the way how to process specific
|
||||||
|
// properties of your component and which values
|
||||||
|
// it can accept.
|
||||||
|
} as ComponentMeta<typeof Container>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a way to define a tempalte for your component.
|
||||||
|
*
|
||||||
|
* This template should cover all the states.
|
||||||
|
*
|
||||||
|
* In most cases you should just distruct args attribute
|
||||||
|
* on a returning component.
|
||||||
|
*/
|
||||||
|
const Template: ComponentStory<typeof Container> = (args) => (
|
||||||
|
<Container {...args}><div className='h-40 bg-yellow-400 flex items-center justify-center uppercase font-bold'>simple</div></Container>
|
||||||
|
);
|
||||||
|
//
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* States of your component */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
export const Desktop: ComponentStory<typeof Container> = Template.bind({});
|
||||||
|
Desktop.parameters = {
|
||||||
|
viewport: {
|
||||||
|
defaultViewport: 'desktop',
|
||||||
|
styles: {
|
||||||
|
height: '568px',
|
||||||
|
width: '320px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}
|
23
src/components/Container.tsx
Normal file
23
src/components/Container.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
import React from "react";
|
||||||
|
type Props = {
|
||||||
|
/**
|
||||||
|
* Content of component
|
||||||
|
*/
|
||||||
|
children: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* Component styling
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
} & Omit<React.ComponentPropsWithoutRef<"div">, "">
|
||||||
|
/**
|
||||||
|
* Main container to handle page content max-width on
|
||||||
|
* different screen sizes
|
||||||
|
*/
|
||||||
|
export default function Container({children, className}: Props) {
|
||||||
|
return (
|
||||||
|
<div className={ classNames('container mx-auto', className) }>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
48
src/components/breadcrumbs.tsx
Normal file
48
src/components/breadcrumbs.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React, { Children, ReactElement } from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
divider: "dotted" | "slash";
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/* ---------- a random number for a unique key (react requirement) ---------- */
|
||||||
|
|
||||||
|
function randomInteger( min: number, max: number ) {
|
||||||
|
let rand = min + Math.random() * (max + 1 - min);
|
||||||
|
return Math.floor(rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------- get the divider as designed ---------------------- */
|
||||||
|
|
||||||
|
function GetDivider( symbol: string ) {
|
||||||
|
const dividers = {
|
||||||
|
'dotted' : <span className='inline-block h-1 w-1 bg-gray-500 rounded m-1 text-center' key={ randomInteger(20, 1000) }></span>,
|
||||||
|
'slash' : <span className='fz-12 f-gray-700 m-1' key={ randomInteger(20, 1000) }>/</span>,
|
||||||
|
}
|
||||||
|
return ( symbol === 'dotted') ? dividers.dotted : dividers.slash
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Breadcrumbs( { divider ,children, className }: Props ) {
|
||||||
|
|
||||||
|
/* -------------- Convert the children into an array and insert ------------- */
|
||||||
|
/* ------------------the elements with the divider into it ------------------ */
|
||||||
|
|
||||||
|
let childrenToArray = React.Children.toArray(children)
|
||||||
|
let LinkAndDivider: any
|
||||||
|
LinkAndDivider = [];
|
||||||
|
|
||||||
|
for( let i = 0; i < childrenToArray.length; i++ ) {
|
||||||
|
|
||||||
|
let dividerComponent = GetDivider( divider )
|
||||||
|
LinkAndDivider = LinkAndDivider.concat( childrenToArray[i] ).concat( dividerComponent )
|
||||||
|
}
|
||||||
|
LinkAndDivider.pop();
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className={ className }>
|
||||||
|
{ LinkAndDivider }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
@ -13,7 +13,10 @@ export {ReactComponent as SVGCite} from "assets/svg/cite.svg";
|
|||||||
export {ReactComponent as SVGCopy} from "assets/svg/copy.svg";
|
export {ReactComponent as SVGCopy} from "assets/svg/copy.svg";
|
||||||
export {ReactComponent as SVGDelete} from "assets/svg/delete.svg";
|
export {ReactComponent as SVGDelete} from "assets/svg/delete.svg";
|
||||||
export {ReactComponent as SVGDownload} from "assets/svg/download.svg";
|
export {ReactComponent as SVGDownload} from "assets/svg/download.svg";
|
||||||
|
export {ReactComponent as SVGEdit1} from "assets/svg/edit1.svg";
|
||||||
|
export {ReactComponent as SVGEdit2} from "assets/svg/edit2.svg";
|
||||||
export {ReactComponent as SVGError} from "assets/svg/error.svg";
|
export {ReactComponent as SVGError} from "assets/svg/error.svg";
|
||||||
|
export {ReactComponent as SVGEye} from "assets/svg/eye.svg";
|
||||||
export {ReactComponent as SVGFavorite} from "assets/svg/favorite.svg";
|
export {ReactComponent as SVGFavorite} from "assets/svg/favorite.svg";
|
||||||
export {ReactComponent as SVGFiletext} from "assets/svg/filetext.svg";
|
export {ReactComponent as SVGFiletext} from "assets/svg/filetext.svg";
|
||||||
export {ReactComponent as SVGFolder} from "assets/svg/folder.svg";
|
export {ReactComponent as SVGFolder} from "assets/svg/folder.svg";
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import _ from "lodash";
|
||||||
export interface DTOModel<T extends {} | undefined> {
|
export interface DTOModel<T extends {} | undefined> {
|
||||||
status: number;
|
status: number;
|
||||||
type: string;
|
type: string;
|
||||||
@ -15,4 +16,16 @@ export interface GraphQLFailureDTO {
|
|||||||
errors: {code: string, message: string, name: string}[]
|
errors: {code: string, message: string, name: string}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDTOModel(obj: any): obj is DTOModel<any> {
|
||||||
|
return obj !== null && typeof obj === "object" && !!obj.isDTOModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isDTO = (obj: any): obj is DTOModel<any> => obj.data !== null &&
|
||||||
|
_.isObject(obj.data) &&
|
||||||
|
_.has(obj.data, "status") &&
|
||||||
|
_.has(obj.data, "type") &&
|
||||||
|
_.has(obj.data, "message") &&
|
||||||
|
_.has(obj.data, "description") &&
|
||||||
|
_.has(obj.data, "data")
|
||||||
|
|
||||||
export const GRAPHQL_UNAUTHORIZED_KEY = "UNAUTHENTICATED";
|
export const GRAPHQL_UNAUTHORIZED_KEY = "UNAUTHENTICATED";
|
@ -1,7 +1,6 @@
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { DTOModel } from "./dto_model";
|
import { DTOModel, isDTO } from "./dto_model";
|
||||||
|
|
||||||
|
|
||||||
export interface FailureI {
|
export interface FailureI {
|
||||||
status?: number;
|
status?: number;
|
||||||
@ -32,21 +31,24 @@ class Failure implements FailureI {
|
|||||||
*/
|
*/
|
||||||
static isFailure(o: any): o is Failure {
|
static isFailure(o: any): o is Failure {
|
||||||
if (!_.isObjectLike(o)) return false;
|
if (!_.isObjectLike(o)) return false;
|
||||||
if(_.has(o, 'message')) return true;
|
if (_.has(o, "message")) return true;
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor({ status, message, key, meta }: FailureI) {
|
constructor({ status, message, key, meta }: FailureI) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.meta = meta;
|
this.meta = meta;
|
||||||
console.error([key ?? '', message].filter((i) => i.length !== 0).join(':'));
|
console.error([key ?? "", message].filter((i) => i.length !== 0).join(":"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromReason(reason: AxiosError<DTOModel<any>>, key?: string): Failure {
|
static fromReason(reason: AxiosError<any, any>, key?: string): Failure {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(!Failure.verifyContainsDTO(reason)) {
|
||||||
|
throw reason;
|
||||||
|
}
|
||||||
if (reason.response) {
|
if (reason.response) {
|
||||||
return new Failure({
|
return new Failure({
|
||||||
status: reason.response!.status,
|
status: reason.response!.status,
|
||||||
@ -68,12 +70,31 @@ class Failure implements FailureI {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
throw reason;
|
||||||
|
} catch (error) {
|
||||||
return new Failure({
|
return new Failure({
|
||||||
message: reason.message,
|
message: reason.message,
|
||||||
key: key,
|
key: key,
|
||||||
meta: { isPlainFailure: true },
|
meta: { isPlainFailure: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that passed object matches [DTOModel] structure
|
||||||
|
*
|
||||||
|
* Otherwise it will rethrow passed object
|
||||||
|
*/
|
||||||
|
static verifyContainsDTO(
|
||||||
|
reason: AxiosError<any, any>
|
||||||
|
): reason is AxiosError<DTOModel<any>, any> {
|
||||||
|
return [reason.response, reason.request].reduce((acc, obj) => {
|
||||||
|
if (acc || isDTO(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return `${this.message} (${this.key ?? "key unknown"}) ${
|
return `${this.message} (${this.key ?? "key unknown"}) ${
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Libraries */
|
/* Libraries */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Misc */
|
/* Misc */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
@ -48,7 +48,7 @@ export default function Dashboard() {
|
|||||||
hasSubscription,
|
hasSubscription,
|
||||||
} = useSubscriptionsViewModel(subscriptionsStore, uiStore);
|
} = useSubscriptionsViewModel(subscriptionsStore, uiStore);
|
||||||
/* ---------------------------------- Hooks --------------------------------- */
|
/* ---------------------------------- Hooks --------------------------------- */
|
||||||
const Wrapper: React.FC = React.useMemo(
|
const Wrapper: React.FC<Omit<React.ComponentPropsWithoutRef<"div">, "">> = React.useMemo(
|
||||||
() => (props) =>
|
() => (props) =>
|
||||||
(
|
(
|
||||||
<div className="grid grid-cols-4 grid-rows-none gap-x-8 gap-y-8">
|
<div className="grid grid-cols-4 grid-rows-none gap-x-8 gap-y-8">
|
||||||
@ -90,7 +90,7 @@ export default function Dashboard() {
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
[]
|
[navigate]
|
||||||
);
|
);
|
||||||
/* --------------------------------- Markup --------------------------------- */
|
/* --------------------------------- Markup --------------------------------- */
|
||||||
if (subscriptionsLoading) {
|
if (subscriptionsLoading) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import debounce from "lodash.debounce";
|
import {debounce} from "lodash";
|
||||||
import { User } from "../domain/userEntity";
|
import { User } from "../domain/userEntity";
|
||||||
import { updateUser } from "../domain/userModel";
|
import { updateUser } from "../domain/userModel";
|
||||||
import { UserStore } from "../domain/userStore";
|
import { UserStore } from "../domain/userStore";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Failure from "core/failure";
|
import Failure from "core/failure";
|
||||||
import debounce from "lodash.debounce";
|
import {debounce} from "lodash";
|
||||||
import { UpdateUserArtifacts } from "user/domain/userModel";
|
import { UpdateUserArtifacts } from "user/domain/userModel";
|
||||||
import { User } from "../domain/userEntity";
|
import { User } from "../domain/userEntity";
|
||||||
import { UserStore } from "../domain/userStore";
|
import { UserStore } from "../domain/userStore";
|
||||||
|
@ -67,7 +67,7 @@ module.exports = {
|
|||||||
function ({ addComponents }) {
|
function ({ addComponents }) {
|
||||||
addComponents({
|
addComponents({
|
||||||
'.container': {
|
'.container': {
|
||||||
maxWidth: '100%',
|
maxWidth: '840px',
|
||||||
paddingLeft: '0.5rem',
|
paddingLeft: '0.5rem',
|
||||||
paddingRight: '0.5rem',
|
paddingRight: '0.5rem',
|
||||||
'@screen sm': {
|
'@screen sm': {
|
||||||
@ -77,13 +77,13 @@ module.exports = {
|
|||||||
maxWidth: 'calc(100% - 30px)',
|
maxWidth: 'calc(100% - 30px)',
|
||||||
},
|
},
|
||||||
'@screen lg': {
|
'@screen lg': {
|
||||||
maxWidth: '960px',
|
maxWidth: '840px',
|
||||||
},
|
},
|
||||||
'@screen xl': {
|
'@screen xl': {
|
||||||
maxWidth: '1080px',
|
maxWidth: '840px',
|
||||||
},
|
},
|
||||||
'@screen 2xl': {
|
'@screen 2xl': {
|
||||||
maxWidth: '1080px',
|
maxWidth: '840px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
"esnext"
|
"esnext",
|
||||||
],
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
@ -19,9 +19,14 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"baseUrl": "./src",
|
"baseUrl": "src",
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src",
|
"src/**/*",
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"storybook-static",
|
||||||
|
"src/**/*.stories.tsx",
|
||||||
|
"src/**/*.test.tsx"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user