Merge pull request #1 from hazemKrimi/structure-updates

Project updates
This commit is contained in:
Hazem Krimi
2023-05-06 18:23:18 +01:00
committed by GitHub
68 changed files with 5967 additions and 13406 deletions
+6
View File
@@ -0,0 +1,6 @@
VITE_GRAPHQL_SUPPORT_API=https://example.com/graphql
VITE_PAYMENT_API=https://example.com/payment/api
VITE_GRAPHQL_SUPPORT_SUBSCRIPTIONS_API=https://example.com/graphql
VITE_GRAPHQL_API=https://example.com/graphql
VITE_STRIPE_PUBLIC_KEY=STRIPE_PUBLIC_KEY
VITE_CLOUDINARY_URL=CLOUDINARY_URL
+1
View File
@@ -0,0 +1 @@
.eslintrc.js
+1 -12
View File
@@ -5,6 +5,7 @@ module.exports = {
'plugin:jest/recommended', 'plugin:jest/recommended',
'plugin:prettier/recommended', 'plugin:prettier/recommended',
'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended',
'plugin:import/recommended'
], ],
plugins: ['react', '@typescript-eslint', 'jest', 'prettier'], plugins: ['react', '@typescript-eslint', 'jest', 'prettier'],
env: { env: {
@@ -54,18 +55,6 @@ module.exports = {
'@typescript-eslint/explicit-module-boundary-types': 0, '@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/no-explicit-any': 0, '@typescript-eslint/no-explicit-any': 0,
'linebreak-style': 'off', 'linebreak-style': 'off',
'jsx-a11y/label-has-associated-control': [
'error',
{
labelComponents: [],
labelAttributes: [],
controlComponents: [],
assert: 'either',
depth: 25,
},
],
'jsx-a11y/control-has-associated-label': 0,
'jsx-a11y/anchor-is-valid': 0,
'prettier/prettier': [ 'prettier/prettier': [
'error', 'error',
{ {
-1
View File
@@ -1 +0,0 @@
describe('Alert test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Avatar test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Box test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Button test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('CheckBox test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Input test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Link test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Modal test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Modal test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Search test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Spinner test suite', () => {});
-1
View File
@@ -1 +0,0 @@
describe('Text test suite', () => {});
+27
View File
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using vite"
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@100;300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<title>Astrobuild</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
+48 -43
View File
@@ -3,40 +3,43 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@apollo/client": "^3.3.14", "@apollo/client": "^3.7.10",
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^11.1.0", "@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^14.4.3",
"@types/jest": "^26.0.15", "@types/jest": "^29.5.0",
"@types/jwt-decode": "^3.1.0", "@types/jwt-decode": "^3.1.0",
"@types/node": "^12.0.0", "@types/node": "^18.15.7",
"@types/react": "^17.0.3", "@types/react": "^18.0.29",
"@types/react-dom": "^17.0.3", "@types/react-dom": "^18.0.11",
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.3.3",
"@types/styled-components": "^5.1.9", "@types/styled-components": "^5.1.26",
"formik": "^2.2.6", "formik": "^2.2.9",
"graphql": "^15.5.0", "graphql": "^16.6.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"react": "^17.0.2", "localforage": "^1.10.0",
"react-dom": "^17.0.2", "match-sorter": "^6.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-elastic-carousel": "^0.11.5", "react-elastic-carousel": "^0.11.5",
"react-flow-renderer": "^9.6.0", "react-router-dom": "^6.9.0",
"react-router-dom": "^5.2.0", "react-to-print": "^2.14.12",
"react-scripts": "4.0.3", "reactflow": "^11.7.0",
"react-to-print": "^2.12.6", "sort-by": "^1.2.0",
"styled-components": "^5.2.3", "styled-components": "^5.3.10",
"subscriptions-transport-ws": "^0.9.19", "subscriptions-transport-ws": "^0.9.19",
"typescript": "^4.1.2", "typescript": "^5.0.2",
"web-vitals": "^1.0.1", "vite-plugin-svgr": "^2.4.0",
"yup": "^0.32.9" "web-vitals": "^3.3.0",
"yup": "^1.0.2"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "vite",
"build": "react-scripts build", "build": "vite build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"generate-main": "graphql-codegen --config codegen-main.yml", "generate-main": "graphql-codegen --config codegen-main.yml",
"generate-support": "graphql-codegen --config codegen-support.yml" "generate-support": "graphql-codegen --config codegen-support.yml",
"lint": "yarn run eslint src --ext .ts,.tsx",
"fix": "yarn lint --fix"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
@@ -51,21 +54,23 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@graphql-codegen/cli": "^1.21.3", "@graphql-codegen/cli": "^3.2.2",
"@graphql-codegen/introspection": "^1.18.2", "@graphql-codegen/introspection": "^3.0.1",
"@graphql-codegen/typescript": "^1.22.0", "@graphql-codegen/typescript": "^3.0.2",
"@graphql-codegen/typescript-operations": "^1.17.16", "@graphql-codegen/typescript-operations": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.21.0", "@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^4.21.0", "@typescript-eslint/parser": "^5.56.0",
"eslint-config-airbnb": "18.2.1", "@vitejs/plugin-react": "^4.0.0",
"eslint-config-airbnb-typescript": "^12.3.1", "eslint-config-airbnb": "19.0.4",
"eslint-config-prettier": "^8.1.0", "eslint-config-airbnb-typescript": "^17.0.0",
"eslint-plugin-import": "2.22.1", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^24.3.4", "eslint-plugin-import": "2.27.5",
"eslint-plugin-jsx-a11y": "6.4.1", "eslint-plugin-jest": "^27.2.1",
"eslint-plugin-prettier": "^3.3.1", "eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-react": "7.21.5", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react-hooks": "1.7.0", "eslint-plugin-react": "7.32.2",
"prettier": "^2.2.1" "eslint-plugin-react-hooks": "4.6.0",
"prettier": "^2.8.7",
"vite": "^4.2.1"
} }
} }
-49
View File
@@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@100;300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<title>Astrobuild</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
+235 -75
View File
@@ -1,10 +1,10 @@
import jwtDecode from 'jwt-decode'; import jwtDecode from 'jwt-decode';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Redirect, Switch } from 'react-router-dom'; import { Routes, Route, Navigate } from 'react-router-dom';
import { useLazyQuery, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useReactiveVar } from '@apollo/client';
import { import {
ProtectedRoute, Protected,
AuthRoute, Public,
Navbar, Navbar,
Sidebar, Sidebar,
Spinner, Spinner,
@@ -78,8 +78,6 @@ const App = () => {
getUserById({ variables: { id } }); getUserById({ variables: { id } });
tokenVar(localStorageToken); tokenVar(localStorageToken);
} }
// eslint-disable-next-line
}, []); }, []);
return !loading ? ( return !loading ? (
@@ -90,111 +88,273 @@ const App = () => {
<Sidebar /> <Sidebar />
</> </>
)} )}
<Switch> <Routes>
<ProtectedRoute path='/' exact> <Route path='/' element={
<Protected>
{role !== 'admin' ? ( {role !== 'admin' ? (
<Redirect to='/project' /> <Navigate to='/project' />
) : ( ) : (
<Redirect to='/clients' /> <Navigate to='/clients' />
)} )}
</ProtectedRoute> </Protected>
<ProtectedRoute path='/project' exact> } />
<Route
path='/project'
element={
<Protected>
<Project /> <Project />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/project/:id' exact> }
/>
<Route
path='/project/:id'
element={
<Protected>
<Project /> <Project />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/payments/:id' exact> }
/>
<Route
path='/payments/:id'
element={
<Protected>
<Payments /> <Payments />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/add-project' exact> }
/>
<Route
path='/add-project'
element={
<Protected>
<AddProject /> <AddProject />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/project-settings/:id' exact> }
/>
<Route
path='/project-settings/:id'
element={
<Protected>
<UpdateProject /> <UpdateProject />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/support-messaging/:project' exact> }
/>
<Route
path='/support-messaging/:project'
element={
<Protected>
<SupportMessaging /> <SupportMessaging />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/support-messaging/:project/:id' exact> }
/>
<Route
path='/support-messaging/:project/:id'
element={
<Protected>
<SupportMessaging /> <SupportMessaging />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/template' exact> }
/>
<Route
path='/template'
element={
<Protected>
<Template /> <Template />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/template/:id' exact> }
/>
<Route
path='/template/:id'
element={
<Protected>
<Template /> <Template />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/add-template' exact> }
/>
<Route
path='/add-template'
element={
<Protected>
<AddTemplate /> <AddTemplate />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/template-settings/:id' exact> }
/>
<Route
path='/template-settings/:id'
element={
<Protected>
<TemplateSettings /> <TemplateSettings />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/add-template' exact> }
/>
<Route
path='/add-template'
element={
<Protected>
<AddTemplate /> <AddTemplate />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/feature' exact> }
/>
<Route
path='/feature'
element={
<Protected>
<Feature /> <Feature />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/feature/:id' exact> }
/>
<Route
path='/feature/:id'
element={
<Protected>
<Feature /> <Feature />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/add-feature' exact> }
/>
<Route
path='/add-feature'
element={
<Protected>
<AddFeature /> <AddFeature />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/feature-settings/:id' exact> }
/>
<Route
path='/feature-settings/:id'
element={
<Protected>
<FeatureSettings /> <FeatureSettings />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/category' exact> }
/>
<Route
path='/category'
element={
<Protected>
<Category /> <Category />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/category/:id' exact> }
/>
<Route
path='/category/:id'
element={
<Protected>
<Category /> <Category />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/add-category' exact> }
/>
<Route
path='/add-category'
element={
<Protected>
<AddCategory /> <AddCategory />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/category-settings/:id' exact> }
/>
<Route
path='/category-settings/:id'
element={
<Protected>
<CategorySettings /> <CategorySettings />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/prototype/:id' exact> }
/>
<Route
path='/prototype/:id'
element={
<Protected>
<Prototype /> <Prototype />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/clients' exact> }
/>
<Route
path='/clients'
element={
<Protected>
<Users /> <Users />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/product-owners' exact> }
/>
<Route
path='/product-owners'
element={
<Protected>
<Users /> <Users />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/developers' exact> }
/>
<Route
path='/developers'
element={
<Protected>
<Users /> <Users />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/create-user/:role' exact> }
/>
<Route
path='/create-user/:role'
element={
<Protected>
<CreateUser /> <CreateUser />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/user-settings/:id' exact> }
/>
<Route
path='/user-settings/:id'
element={
<Protected>
<UserSettings /> <UserSettings />
</ProtectedRoute> </Protected>
<ProtectedRoute path='/settings' exact> }
/>
<Route
path='/settings'
element={
<Protected>
<Settings /> <Settings />
</ProtectedRoute> </Protected>
<AuthRoute path='/login' exact> }
/>
<Route
path='/login'
element={
<Public>
<Login /> <Login />
</AuthRoute> </Public>
<AuthRoute path='/signup' exact> }
/>
<Route
path='/signup'
element={
<Public>
<Signup /> <Signup />
</AuthRoute> </Public>
<ProtectedRoute path='/additional-info' exact> }
/>
<Route
path='/additional-info'
element={
<Protected>
<AdditionalInfo /> <AdditionalInfo />
</ProtectedRoute> </Protected>
<AuthRoute path='/forgot-password' exact> }
/>
<Route
path='/forgot-password'
element={
<Public>
<ForgotPassword /> <ForgotPassword />
</AuthRoute> </Public>
<AuthRoute path='/recover-account' exact> }
/>
<Route
path='/recover-account'
element={
<Public>
<RecoverAccount /> <RecoverAccount />
</AuthRoute> </Public>
</Switch> }
/>
</Routes>
</> </>
) : ( ) : (
<Spinner fullScreen color={role || 'client'} /> <Spinner fullScreen color={role || 'client'} />
-13
View File
@@ -1,13 +0,0 @@
import { useReactiveVar } from '@apollo/client';
import { Redirect, Route, RouteProps } from 'react-router-dom';
import { tokenVar } from '../../graphql/state';
const AuthRoute: React.FC<RouteProps> = ({ children, ...rest }) => {
const token = useReactiveVar(tokenVar);
return (
<Route {...rest} render={() => (!token ? children : <Redirect to='/' />)} />
);
};
export default AuthRoute;
+2 -2
View File
@@ -6,8 +6,8 @@ type ButtonProps = {
size?: 'small' | 'big'; size?: 'small' | 'big';
variant?: 'primary-action' | 'secondary-action' | 'outlined' | 'text'; variant?: 'primary-action' | 'secondary-action' | 'outlined' | 'text';
type?: 'submit' | 'button' | 'reset'; type?: 'submit' | 'button' | 'reset';
iconLeft?: React.SVGProps<SVGSVGElement>; iconLeft?: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
iconRight?: React.SVGProps<SVGSVGElement>; iconRight?: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
fullWidth?: boolean; fullWidth?: boolean;
loading?: boolean; loading?: boolean;
disabled?: boolean; disabled?: boolean;
+2 -2
View File
@@ -4,8 +4,8 @@ type WrapperProps = {
color: 'client' | 'productOwner' | 'developer' | 'admin' | 'error'; color: 'client' | 'productOwner' | 'developer' | 'admin' | 'error';
size?: 'small' | 'big'; size?: 'small' | 'big';
variant?: 'primary-action' | 'secondary-action' | 'outlined' | 'text'; variant?: 'primary-action' | 'secondary-action' | 'outlined' | 'text';
iconLeft?: React.SVGProps<SVGSVGElement>; iconLeft?: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
iconRight?: React.SVGProps<SVGSVGElement>; iconRight?: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
load?: boolean; load?: boolean;
disabled?: boolean; disabled?: boolean;
fullWidth?: boolean; fullWidth?: boolean;
+12 -20
View File
@@ -10,46 +10,38 @@ type ContextMenuProps = {
const ContextMenu = ({ items, component, className }: ContextMenuProps) => { const ContextMenu = ({ items, component, className }: ContextMenuProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null); const parentComponentRef = useRef<HTMLDivElement>();
useEffect(() => { useEffect(() => {
const wrapper = ref.current; parentComponentRef.current = document.querySelector(`#${component}`) as HTMLDivElement;
const openMenu = () => setOpen(true); const openMenu = () => setOpen(true);
const closeMenu = () => setOpen(false); const closeMenu = () => setOpen(false);
(document.querySelector(`#${component}`) as HTMLElement)?.addEventListener( parentComponentRef.current?.addEventListener(
'mouseenter', 'mouseenter',
openMenu openMenu
); );
ref.current?.addEventListener('mouseleave', closeMenu); parentComponentRef.current?.addEventListener(
'mouseleave',
closeMenu
);
return () => { return () => {
(document.querySelector( parentComponentRef.current?.removeEventListener('mouseenter', openMenu);
`#${component}` parentComponentRef.current?.removeEventListener('mouseleave', closeMenu);
) as HTMLElement)?.removeEventListener('mouseenter', openMenu);
wrapper?.removeEventListener('mouseleave', closeMenu);
}; };
}, []);
// eslint-disable-next-line
}, [ref.current]);
return ( return (
<Wrapper <Wrapper
ref={ref}
className={className} className={className}
top={ top={(parentComponentRef.current as HTMLDivElement)?.getBoundingClientRect().top + 30}
(document.querySelector(`#${component}`) as HTMLElement)?.offsetTop + 30 left={(parentComponentRef.current as HTMLDivElement)?.getBoundingClientRect().left + 10}
}
left={
(document.querySelector(`#${component}`) as HTMLElement)?.offsetLeft +
10
}
> >
{open && ( {open && (
<ul> <ul>
{items.map(({ label, action }) => ( {items.map(({ label, action }) => (
// eslint-disable-next-line
<li <li
onClick={() => { onClick={() => {
if (action) { if (action) {
+11 -4
View File
@@ -1,16 +1,21 @@
import { Handle, Position } from 'reactflow';
import { Box, Text } from '..'; import { Box, Text } from '..';
import { FeatureOutput } from '../../graphql/types'; import { FeatureOutput } from '../../graphql/types';
type FrontendFeatureCardProps = { type FrontendFeatureCardProps = {
feature: FeatureOutput; data: FeatureOutput;
isConnectable?: boolean;
className?: string; className?: string;
}; };
const FrontendFeatureCard = ({ const FrontendFeatureCard = ({
feature, data,
isConnectable = false,
className, className,
}: FrontendFeatureCardProps) => { }: FrontendFeatureCardProps) => {
return ( return (
<>
<Handle type="target" position={Position.Top} isConnectable={isConnectable} />
<Box <Box
className={className} className={className}
padding='10px' padding='10px'
@@ -27,7 +32,7 @@ const FrontendFeatureCard = ({
<Box display='flex' flexDirection='row' alignItems='center'> <Box display='flex' flexDirection='row' alignItems='center'>
<Box flexGrow='1'> <Box flexGrow='1'>
<Text variant='title' weight='bold'> <Text variant='title' weight='bold'>
{feature.name} {data.name}
</Text> </Text>
</Box> </Box>
</Box> </Box>
@@ -38,7 +43,7 @@ const FrontendFeatureCard = ({
justifyContent='space-between' justifyContent='space-between'
padding='5px 20px' padding='5px 20px'
> >
{feature.wireframes?.map((wireframe) => ( {data.wireframes?.map((wireframe) => (
<img <img
src={wireframe.src} src={wireframe.src}
alt={wireframe.name} alt={wireframe.name}
@@ -48,6 +53,8 @@ const FrontendFeatureCard = ({
))} ))}
</Box> </Box>
</Box> </Box>
<Handle type="source" position={Position.Bottom} isConnectable={isConnectable} />
</>
); );
}; };
+1 -1
View File
@@ -3,7 +3,7 @@ import { Wrapper } from './styles';
type IconButtonProps = { type IconButtonProps = {
color?: 'client' | 'productOwner' | 'developer' | 'admin'; color?: 'client' | 'productOwner' | 'developer' | 'admin';
size?: 'small' | 'medium' | 'big'; size?: 'small' | 'medium' | 'big';
icon?: React.SVGProps<SVGSVGElement>; icon?: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
onClick: () => void; onClick: () => void;
}; };
+1 -1
View File
@@ -3,7 +3,7 @@ import styled, { css } from 'styled-components';
type WrapperProps = { type WrapperProps = {
color?: 'client' | 'productOwner' | 'developer' | 'admin'; color?: 'client' | 'productOwner' | 'developer' | 'admin';
size?: 'small' | 'medium' | 'big'; size?: 'small' | 'medium' | 'big';
icon?: React.SVGProps<SVGSVGElement>; icon?: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
}; };
export const Wrapper = styled.button<WrapperProps>` export const Wrapper = styled.button<WrapperProps>`
+1 -1
View File
@@ -18,7 +18,7 @@ type LinkProps = {
| string; | string;
selected?: boolean; selected?: boolean;
className?: string; className?: string;
iconLeft?: React.SVGProps<SVGSVGElement>; iconLeft?: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
onClick?: () => void; onClick?: () => void;
target?: '_self' | '_blank'; target?: '_self' | '_blank';
}; };
+14 -21
View File
@@ -5,7 +5,7 @@ import { Text } from '..';
type MenuProps = { type MenuProps = {
className?: string; className?: string;
items: Array<{ items: Array<{
icon: React.SVGProps<SVGSVGElement>; icon: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
avoid?: boolean; avoid?: boolean;
label: string; label: string;
action?: () => void; action?: () => void;
@@ -15,45 +15,38 @@ type MenuProps = {
const Menu = ({ items, component, className }: MenuProps) => { const Menu = ({ items, component, className }: MenuProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null); const componentRef = useRef<HTMLDivElement>(null);
const parentComponentRef = useRef<HTMLDivElement>();
useEffect(() => {
const wrapper = ref.current;
const openMenu = () => setOpen(true); const openMenu = () => setOpen(true);
const closeMenu = () => setOpen(false); const closeMenu = () => setOpen(false);
(document.querySelector(`#${component}`) as HTMLElement)?.addEventListener( useEffect(() => {
'mouseenter', parentComponentRef.current = document.querySelector(`#${component}`) as HTMLDivElement;
openMenu
); parentComponentRef.current?.addEventListener('mouseenter', openMenu);
ref.current?.addEventListener('mouseleave', closeMenu); componentRef.current?.addEventListener('mouseleave', closeMenu);
return () => { return () => {
(document.querySelector( parentComponentRef.current?.removeEventListener('mouseenter', openMenu);
`#${component}` componentRef.current?.removeEventListener('mouseleave', closeMenu);
) as HTMLElement)?.removeEventListener('mouseenter', openMenu);
wrapper?.addEventListener('mouseleave', closeMenu);
}; };
}, []);
// eslint-disable-next-line
}, [ref.current]);
return ( return (
<Wrapper <Wrapper
ref={ref} ref={componentRef}
className={className} className={className}
top={ top={
(document.querySelector(`#${component}`) as HTMLElement)?.offsetTop + 30 (parentComponentRef.current as HTMLDivElement)?.getBoundingClientRect().top + 30
} }
left={ left={
(document.querySelector(`#${component}`) as HTMLElement)?.offsetLeft (parentComponentRef.current as HTMLDivElement)?.getBoundingClientRect()?.left
} }
> >
{open && ( {open && (
<ul> <ul>
{items.map(({ icon, label, avoid, action }) => ( {items.map(({ icon, label, avoid, action }) => (
// eslint-disable-next-line
<li <li
onClick={() => { onClick={() => {
if (action) { if (action) {
+4 -6
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useHistory, useLocation } from 'react-router'; import { useNavigate, useLocation } from 'react-router';
import { useReactiveVar } from '@apollo/client'; import { useReactiveVar } from '@apollo/client';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Box, Button, Text } from '..'; import { Box, Button, Text } from '..';
@@ -20,7 +20,7 @@ type MessagingSidebarProps = {
const MessagingSidebar = ({ onClose }: MessagingSidebarProps) => { const MessagingSidebar = ({ onClose }: MessagingSidebarProps) => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const location = useLocation(); const location = useLocation();
const history = useHistory(); const navigate = useNavigate();
const [projectThreads, setProjectThreads] = useState<Array<ThreadObject>>(); const [projectThreads, setProjectThreads] = useState<Array<ThreadObject>>();
useEffect(() => { useEffect(() => {
@@ -65,9 +65,7 @@ const MessagingSidebar = ({ onClose }: MessagingSidebarProps) => {
iconLeft={<Add />} iconLeft={<Add />}
onClick={() => { onClick={() => {
onClose(); onClose();
history.push( navigate(`/support-messaging/${location.pathname.split('/')[2]}`);
`/support-messaging/${location.pathname.split('/')[2]}`
);
}} }}
/> />
</Box> </Box>
@@ -87,7 +85,7 @@ const MessagingSidebar = ({ onClose }: MessagingSidebarProps) => {
borderRadius='10px' borderRadius='10px'
onClick={() => { onClick={() => {
onClose(); onClose();
history.push( navigate(
`/support-messaging/${location.pathname.split('/')[2]}/${ `/support-messaging/${location.pathname.split('/')[2]}/${
thread.id thread.id
}` }`
+4 -4
View File
@@ -1,5 +1,5 @@
import { useReactiveVar } from '@apollo/client'; import { useReactiveVar } from '@apollo/client';
import { useHistory, useLocation } from 'react-router'; import { useNavigate, useLocation } from 'react-router';
import { roleVar, tokenVar, userVar } from '../../graphql/state'; import { roleVar, tokenVar, userVar } from '../../graphql/state';
import { Wrapper } from './styles'; import { Wrapper } from './styles';
import { Avatar, Link, Menu, Text } from '..'; import { Avatar, Link, Menu, Text } from '..';
@@ -8,7 +8,7 @@ import { Settings, Logout, Logo } from '../../assets';
const Navbar = () => { const Navbar = () => {
const user = useReactiveVar(userVar); const user = useReactiveVar(userVar);
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
return ( return (
@@ -151,7 +151,7 @@ const Navbar = () => {
{ {
icon: <Settings />, icon: <Settings />,
label: 'Settings', label: 'Settings',
action: () => history.push('/settings'), action: () => navigate('/settings'),
}, },
{ {
icon: <Logout />, icon: <Logout />,
@@ -159,7 +159,7 @@ const Navbar = () => {
action: () => { action: () => {
tokenVar(undefined); tokenVar(undefined);
localStorage.removeItem('token'); localStorage.removeItem('token');
history.push('/login'); navigate('/login');
}, },
avoid: true, avoid: true,
}, },
+19
View File
@@ -0,0 +1,19 @@
import { useReactiveVar } from '@apollo/client';
import { Navigate } from 'react-router-dom';
import { tokenVar } from '../../graphql/state';
type Props = {
children: React.ReactNode;
};
const Protected = ({ children }: Props) => {
const token = useReactiveVar(tokenVar);
return (
<>
{token ? children : <Navigate to='/login' />}
</>
);
};
export default Protected;
-16
View File
@@ -1,16 +0,0 @@
import { useReactiveVar } from '@apollo/client';
import { Redirect, Route, RouteProps } from 'react-router-dom';
import { tokenVar } from '../../graphql/state';
const ProtectedRoute: React.FC<RouteProps> = ({ children, ...rest }) => {
const token = useReactiveVar(tokenVar);
return (
<Route
{...rest}
render={() => (token ? children : <Redirect to='/login' />)}
/>
);
};
export default ProtectedRoute;
+19
View File
@@ -0,0 +1,19 @@
import { useReactiveVar } from '@apollo/client';
import { Navigate } from 'react-router-dom';
import { tokenVar } from '../../graphql/state';
type Props = {
children: React.ReactNode;
};
const Public = ({ children }: Props) => {
const token = useReactiveVar(tokenVar);
return (
<>
{!token ? children : <Navigate to='/' />}
</>
);
};
export default Public;
+1 -1
View File
@@ -1,7 +1,7 @@
import { Wrapper } from './styles'; import { Wrapper } from './styles';
type SectionSelectorProps = { type SectionSelectorProps = {
icon: React.SVGProps<SVGSVGElement>; icon: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
text: string; text: string;
color: 'client' | 'productOwner' | 'developer' | 'admin'; color: 'client' | 'productOwner' | 'developer' | 'admin';
selected?: boolean; selected?: boolean;
+1 -1
View File
@@ -1,7 +1,7 @@
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
type WrapperProps = { type WrapperProps = {
icon: React.SVGProps<SVGSVGElement>; icon: React.FunctionComponentElement<React.SVGProps<SVGSVGElement>>;
color: 'client' | 'productOwner' | 'developer' | 'admin'; color: 'client' | 'productOwner' | 'developer' | 'admin';
selected: boolean; selected: boolean;
disabled: boolean; disabled: boolean;
+12 -13
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useHistory, useLocation } from 'react-router'; import { useNavigate, useLocation } from 'react-router';
import { useLazyQuery, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useReactiveVar } from '@apollo/client';
import { roleVar, userVar } from '../../graphql/state'; import { roleVar, userVar } from '../../graphql/state';
import { import {
@@ -39,14 +39,13 @@ const Sidebar = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const currentUser = useReactiveVar(userVar); const currentUser = useReactiveVar(userVar);
const location = useLocation(); const location = useLocation();
const history = useHistory(); const navigate = useNavigate();
const [projects, setProjects] = useState<Array<ProjectOutput>>(); const [projects, setProjects] = useState<Array<ProjectOutput>>();
const [templates, setTemplates] = useState<Array<TemplateOutput>>(); const [templates, setTemplates] = useState<Array<TemplateOutput>>();
const [features, setFeatures] = useState<Array<FeatureOutput>>(); const [features, setFeatures] = useState<Array<FeatureOutput>>();
const [categories, setCategories] = useState<Array<CategoryOutput>>(); const [categories, setCategories] = useState<Array<CategoryOutput>>();
const [messagingSidebarOpen, setMessagingSidebarOpen] = useState<boolean>( const [messagingSidebarOpen, setMessagingSidebarOpen] =
false useState<boolean>(false);
);
const [getProjectsByClientId] = useLazyQuery< const [getProjectsByClientId] = useLazyQuery<
GetAllProjectsByClientIdQuery, GetAllProjectsByClientIdQuery,
@@ -143,7 +142,7 @@ const Sidebar = () => {
location.pathname location.pathname
)} )}
text={project.name[0]} text={project.name[0]}
onClick={() => history.push(`/project/${project.id}`)} onClick={() => navigate(`/project/${project.id}`)}
/> />
</div> </div>
<ContextMenu <ContextMenu
@@ -163,7 +162,7 @@ const Sidebar = () => {
(index === 0 && location.pathname === '/template') (index === 0 && location.pathname === '/template')
} }
text={template.name[0]} text={template.name[0]}
onClick={() => history.push(`/template/${template.id}`)} onClick={() => navigate(`/template/${template.id}`)}
/> />
</div> </div>
<ContextMenu <ContextMenu
@@ -183,7 +182,7 @@ const Sidebar = () => {
(index === 0 && location.pathname === '/feature') (index === 0 && location.pathname === '/feature')
} }
text={feature.name[0]} text={feature.name[0]}
onClick={() => history.push(`/feature/${feature.id}`)} onClick={() => navigate(`/feature/${feature.id}`)}
/> />
</div> </div>
<ContextMenu <ContextMenu
@@ -203,7 +202,7 @@ const Sidebar = () => {
(index === 0 && location.pathname === '/category') (index === 0 && location.pathname === '/category')
} }
text={category.name[0]} text={category.name[0]}
onClick={() => history.push(`/category/${category.id}`)} onClick={() => navigate(`/category/${category.id}`)}
/> />
</div> </div>
<ContextMenu <ContextMenu
@@ -220,16 +219,16 @@ const Sidebar = () => {
color={role} color={role}
onClick={() => { onClick={() => {
if (/project/i.test(location.pathname)) { if (/project/i.test(location.pathname)) {
history.push('/add-project'); navigate('/add-project');
} }
if (/template/i.test(location.pathname)) { if (/template/i.test(location.pathname)) {
history.push('/add-template'); navigate('/add-template');
} }
if (/feature/i.test(location.pathname)) { if (/feature/i.test(location.pathname)) {
history.push('/add-feature'); navigate('/add-feature');
} }
if (/category/i.test(location.pathname)) { if (/category/i.test(location.pathname)) {
history.push('/add-category'); navigate('/add-category');
} }
}} }}
/> />
+5
View File
@@ -17,4 +17,9 @@ export const Wrapper = styled.div<WrapperProps>`
grid-template-rows: 1fr auto; grid-template-rows: 1fr auto;
justify-content: center; justify-content: center;
padding: 55px 0px; padding: 55px 0px;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 1px;
}
`; `;
+4 -4
View File
@@ -15,8 +15,8 @@ import CheckBox from './CheckBox';
import Menu from './Menu'; import Menu from './Menu';
import Navbar from './Navbar'; import Navbar from './Navbar';
import Sidebar from './Sidebar'; import Sidebar from './Sidebar';
import ProtectedRoute from './ProtectedRoute'; import Protected from './Protected';
import AuthRoute from './AuthRoute'; import Public from './Public';
import SectionSelector from './SectionSelector'; import SectionSelector from './SectionSelector';
import Modal from './Modal'; import Modal from './Modal';
import SidebarItem from './SidebarItem'; import SidebarItem from './SidebarItem';
@@ -48,8 +48,8 @@ export {
CheckBox, CheckBox,
Navbar, Navbar,
Sidebar, Sidebar,
ProtectedRoute, Protected,
AuthRoute, Public,
SectionSelector, SectionSelector,
Modal, Modal,
SidebarItem, SidebarItem,
+61 -67
View File
@@ -1,7 +1,13 @@
export type Maybe<T> = T | null; export type Maybe<T> = T | null;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] }; export type Exact<T extends { [key: string]: unknown }> = {
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> }; [K in keyof T]: T[K];
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> }; };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]?: Maybe<T[SubKey]>;
};
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]: Maybe<T[SubKey]>;
};
/** All built-in and custom scalars, mapped to their actual values */ /** All built-in and custom scalars, mapped to their actual values */
export type Scalars = { export type Scalars = {
ID: string; ID: string;
@@ -11,7 +17,6 @@ export type Scalars = {
Float: number; Float: number;
}; };
export type MutationRoot = { export type MutationRoot = {
__typename?: 'MutationRoot'; __typename?: 'MutationRoot';
createThread: Scalars['ID']; createThread: Scalars['ID'];
@@ -19,27 +24,23 @@ export type MutationRoot = {
sendMsg: Scalars['ID']; sendMsg: Scalars['ID'];
}; };
export type MutationRootCreateThreadArgs = { export type MutationRootCreateThreadArgs = {
projectId: Scalars['String']; projectId: Scalars['String'];
title: Scalars['String']; title: Scalars['String'];
threadDescription: Scalars['String']; threadDescription: Scalars['String'];
}; };
export type MutationRootDeleteThreadArgs = { export type MutationRootDeleteThreadArgs = {
threadId: Scalars['String']; threadId: Scalars['String'];
}; };
export type MutationRootSendMsgArgs = { export type MutationRootSendMsgArgs = {
threadId: Scalars['String']; threadId: Scalars['String'];
username: Scalars['String']; username: Scalars['String'];
msg: Scalars['String']; msg: Scalars['String'];
}; };
export type MutationType = export type MutationType = 'CREATED';
| 'CREATED';
export type QueryRoot = { export type QueryRoot = {
__typename?: 'QueryRoot'; __typename?: 'QueryRoot';
@@ -48,17 +49,14 @@ export type QueryRoot = {
getThreadById: ThreadObject; getThreadById: ThreadObject;
}; };
export type QueryRootMessagesArgs = { export type QueryRootMessagesArgs = {
threadId: Scalars['String']; threadId: Scalars['String'];
}; };
export type QueryRootGetProjectThreadsArgs = { export type QueryRootGetProjectThreadsArgs = {
projectId: Scalars['String']; projectId: Scalars['String'];
}; };
export type QueryRootGetThreadByIdArgs = { export type QueryRootGetThreadByIdArgs = {
threadId: Scalars['String']; threadId: Scalars['String'];
}; };
@@ -75,7 +73,6 @@ export type SubscriptionRoot = {
connectStream: StreamChanged; connectStream: StreamChanged;
}; };
export type SubscriptionRootConnectStreamArgs = { export type SubscriptionRootConnectStreamArgs = {
mutationType?: Maybe<MutationType>; mutationType?: Maybe<MutationType>;
}; };
@@ -105,48 +102,46 @@ export type GetProjectThreadsQueryVariables = Exact<{
projectId: Scalars['String']; projectId: Scalars['String'];
}>; }>;
export type GetProjectThreadsQuery = { __typename?: 'QueryRoot' } & {
export type GetProjectThreadsQuery = ( getProjectThreads: Array<
{ __typename?: 'QueryRoot' } { __typename?: 'ThreadObject' } & Pick<
& { getProjectThreads: Array<( ThreadObject,
{ __typename?: 'ThreadObject' } 'id' | 'title' | 'threadDescription'
& Pick<ThreadObject, 'id' | 'title' | 'threadDescription'> > & {
& { userMessages: Array<( userMessages: Array<
{ __typename?: 'UserMessage' } { __typename?: 'UserMessage' } & Pick<
& Pick<UserMessage, 'username' | 'text'> UserMessage,
)> } 'username' | 'text'
)> } >
); >;
}
>;
};
export type GetThreadByIdQueryVariables = Exact<{ export type GetThreadByIdQueryVariables = Exact<{
threadId: Scalars['String']; threadId: Scalars['String'];
}>; }>;
export type GetThreadByIdQuery = { __typename?: 'QueryRoot' } & {
export type GetThreadByIdQuery = ( getThreadById: { __typename?: 'ThreadObject' } & Pick<
{ __typename?: 'QueryRoot' } ThreadObject,
& { getThreadById: ( 'id' | 'title' | 'threadDescription'
{ __typename?: 'ThreadObject' } > & {
& Pick<ThreadObject, 'id' | 'title' | 'threadDescription'> userMessages: Array<
& { userMessages: Array<( { __typename?: 'UserMessage' } & Pick<UserMessage, 'username' | 'text'>
{ __typename?: 'UserMessage' } >;
& Pick<UserMessage, 'username' | 'text'> };
)> } };
) }
);
export type MessagesQueryVariables = Exact<{ export type MessagesQueryVariables = Exact<{
threadId: Scalars['String']; threadId: Scalars['String'];
}>; }>;
export type MessagesQuery = { __typename?: 'QueryRoot' } & {
export type MessagesQuery = ( messages: Array<
{ __typename?: 'QueryRoot' } { __typename?: 'UserMessage' } & Pick<UserMessage, 'username' | 'text'>
& { messages: Array<( >;
{ __typename?: 'UserMessage' } };
& Pick<UserMessage, 'username' | 'text'>
)> }
);
export type CreateThreadMutationVariables = Exact<{ export type CreateThreadMutationVariables = Exact<{
projectId: Scalars['String']; projectId: Scalars['String'];
@@ -154,11 +149,10 @@ export type CreateThreadMutationVariables = Exact<{
threadDescription: Scalars['String']; threadDescription: Scalars['String'];
}>; }>;
export type CreateThreadMutation = { __typename?: 'MutationRoot' } & Pick<
export type CreateThreadMutation = ( MutationRoot,
{ __typename?: 'MutationRoot' } 'createThread'
& Pick<MutationRoot, 'createThread'> >;
);
export type SendMsgMutationVariables = Exact<{ export type SendMsgMutationVariables = Exact<{
threadId: Scalars['String']; threadId: Scalars['String'];
@@ -166,25 +160,25 @@ export type SendMsgMutationVariables = Exact<{
msg: Scalars['String']; msg: Scalars['String'];
}>; }>;
export type SendMsgMutation = { __typename?: 'MutationRoot' } & Pick<
export type SendMsgMutation = ( MutationRoot,
{ __typename?: 'MutationRoot' } 'sendMsg'
& Pick<MutationRoot, 'sendMsg'> >;
);
export type ConnectStreamSubscriptionVariables = Exact<{ export type ConnectStreamSubscriptionVariables = Exact<{
mutationType?: Maybe<MutationType>; mutationType?: Maybe<MutationType>;
}>; }>;
export type ConnectStreamSubscription = { __typename?: 'SubscriptionRoot' } & {
export type ConnectStreamSubscription = ( connectStream: { __typename?: 'StreamChanged' } & Pick<
{ __typename?: 'SubscriptionRoot' } StreamChanged,
& { connectStream: ( 'mutationType' | 'id'
{ __typename?: 'StreamChanged' } > & {
& Pick<StreamChanged, 'mutationType' | 'id'> userMessage?: Maybe<
& { userMessage?: Maybe<( { __typename?: 'UserMessageObject' } & Pick<
{ __typename?: 'UserMessageObject' } UserMessageObject,
& Pick<UserMessageObject, 'id' | 'username' | 'text'> 'id' | 'username' | 'text'
)> } >
) } >;
); };
};
+2068 -1521
View File
File diff suppressed because it is too large Load Diff
+17 -8
View File
@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import * as ReactDOMClient from 'react-dom/client';
import { import {
ApolloClient, ApolloClient,
InMemoryCache, InMemoryCache,
@@ -18,15 +18,15 @@ import GlobalStyles from './GlobalStyles';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
const httpLinkMain = createHttpLink({ const httpLinkMain = createHttpLink({
uri: process.env.REACT_APP_GRAPHQL_API, uri: import.meta.env.VITE_GRAPHQL_API,
}); });
const httpLinkSupport = createHttpLink({ const httpLinkSupport = createHttpLink({
uri: process.env.REACT_APP_GRAPHQL_SUPPORT_API, uri: import.meta.env.VITE_GRAPHQL_SUPPORT_API,
}); });
const wsLink = new WebSocketLink({ const wsLink = new WebSocketLink({
uri: `${process.env.REACT_APP_GRAPHQL_SUPPORT_SUBSCRIPTIONS_API}`, uri: `${import.meta.env.VITE_GRAPHQL_SUPPORT_SUBSCRIPTIONS_API}`,
options: { options: {
reconnect: true, reconnect: true,
}, },
@@ -65,19 +65,28 @@ export const clientSupport = new ApolloClient({
cache: new InMemoryCache(), cache: new InMemoryCache(),
}); });
ReactDOM.render( let root: ReactDOMClient.Root | null = null;
document.addEventListener('DOMContentLoaded', () => {
if (!root) {
root = ReactDOMClient.createRoot(document.querySelector('#app') as HTMLElement);
root.render(
<React.StrictMode> <React.StrictMode>
<ApolloProvider client={clientMain}> <ApolloProvider client={clientMain}>
{/* @ts-ignore */}
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<BrowserRouter> <BrowserRouter>
<App /> <App />
{/* @ts-ignore */}
<GlobalStyles /> <GlobalStyles />
</BrowserRouter> </BrowserRouter>
</ThemeProvider> </ThemeProvider>
</ApolloProvider> </ApolloProvider>
</React.StrictMode>, </React.StrictMode>
document.getElementById('root') )
); }
});
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log)) // to log results (for example: reportWebVitals(console.log))
+8 -8
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory } from 'react-router'; import { Navigate, useNavigate } from 'react-router';
import { useMutation, useReactiveVar } from '@apollo/client'; import { useMutation, useReactiveVar } from '@apollo/client';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
@@ -22,7 +22,7 @@ import {
import { ADD_CATEGORY } from '../../graphql/category.api'; import { ADD_CATEGORY } from '../../graphql/category.api';
const AddCategory = () => { const AddCategory = () => {
const history = useHistory(); const navigate = useNavigate();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
@@ -32,10 +32,10 @@ const AddCategory = () => {
AddCategoryMutationVariables AddCategoryMutationVariables
>(ADD_CATEGORY, { >(ADD_CATEGORY, {
onCompleted({ addCategory: { id } }) { onCompleted({ addCategory: { id } }) {
history.push(`/category/${id}`); navigate(`/category/${id}`);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -73,7 +73,7 @@ const AddCategory = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -144,7 +144,7 @@ const AddCategory = () => {
formData.append('upload_preset', 'xofll5kc'); formData.append('upload_preset', 'xofll5kc');
const data = await ( const data = await (
await fetch(`${process.env.REACT_APP_CLOUDINARY_URL}`, { await fetch(`${import.meta.env.VITE_CLOUDINARY_URL}`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}) })
@@ -190,9 +190,9 @@ const AddCategory = () => {
</Wrapper> </Wrapper>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'productOwner' && <Redirect to='/project' />)} (role === 'productOwner' && <Navigate to='/project' />)}
</> </>
); );
}; };
+9 -9
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory } from 'react-router'; import { Navigate, useNavigate } from 'react-router';
import { useMutation, useReactiveVar } from '@apollo/client'; import { useMutation, useReactiveVar } from '@apollo/client';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
@@ -24,7 +24,7 @@ import {
import { ADD_FEATURE } from '../../graphql/feature.api'; import { ADD_FEATURE } from '../../graphql/feature.api';
const AddFeature = () => { const AddFeature = () => {
const history = useHistory(); const navigate = useNavigate();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const [newFeature, setNewFeature] = useState<{ const [newFeature, setNewFeature] = useState<{
name: string; name: string;
@@ -62,10 +62,10 @@ const AddFeature = () => {
AddFeatureMutationVariables AddFeatureMutationVariables
>(ADD_FEATURE, { >(ADD_FEATURE, {
onCompleted({ addFeature: { id } }) { onCompleted({ addFeature: { id } }) {
history.push(`/feature/${id}`); navigate(`/feature/${id}`);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -129,7 +129,7 @@ const AddFeature = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -214,7 +214,7 @@ const AddFeature = () => {
const data = await ( const data = await (
await fetch( await fetch(
`${process.env.REACT_APP_CLOUDINARY_URL}`, `${import.meta.env.VITE_CLOUDINARY_URL}`,
{ {
method: 'POST', method: 'POST',
body: formData, body: formData,
@@ -428,7 +428,7 @@ const AddFeature = () => {
const data = await ( const data = await (
await fetch( await fetch(
`${process.env.REACT_APP_CLOUDINARY_URL}`, `${import.meta.env.VITE_CLOUDINARY_URL}`,
{ {
method: 'POST', method: 'POST',
body: formData, body: formData,
@@ -479,9 +479,9 @@ const AddFeature = () => {
</Wrapper> </Wrapper>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'productOwner' && <Redirect to='/project' />)} (role === 'productOwner' && <Navigate to='/project' />)}
</> </>
); );
}; };
+24 -27
View File
@@ -2,7 +2,7 @@
import Carousel, { consts } from 'react-elastic-carousel'; import Carousel, { consts } from 'react-elastic-carousel';
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { roleVar, userVar } from '../../graphql/state'; import { roleVar, userVar } from '../../graphql/state';
@@ -64,7 +64,7 @@ import { CREATE_USER, GET_ALL_USERS } from '../../graphql/admin.api';
import { GET_COUNTRY_CODES } from '../../graphql/auth.api'; import { GET_COUNTRY_CODES } from '../../graphql/auth.api';
const AddProject = () => { const AddProject = () => {
const history = useHistory(); const navigate = useNavigate();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const currentUser = useReactiveVar(userVar); const currentUser = useReactiveVar(userVar);
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
@@ -86,14 +86,10 @@ const AddProject = () => {
const [chosenFeatures, setChosenFeatures] = useState<Array<FeatureOutput>>( const [chosenFeatures, setChosenFeatures] = useState<Array<FeatureOutput>>(
[] []
); );
const [ const [chosenDeliverables, setChosenDeliverables] =
chosenDeliverables, useState<DelivrableInput>();
setChosenDeliverables, const [chosenPaymentOption, setChosenPaymentOption] =
] = useState<DelivrableInput>(); useState<PaymentOptionInput>();
const [
chosenPaymentOption,
setChosenPaymentOption,
] = useState<PaymentOptionInput>();
const [chosenPlatforms, setChosenPlatforms] = useState<Array<string>>([]); const [chosenPlatforms, setChosenPlatforms] = useState<Array<string>>([]);
const [selectedFeature, setSelectedFeature] = useState<FeatureOutput>(); const [selectedFeature, setSelectedFeature] = useState<FeatureOutput>();
const [categories, setCategories] = useState<Array<CategoryOutput>>([]); const [categories, setCategories] = useState<Array<CategoryOutput>>([]);
@@ -200,23 +196,21 @@ const AddProject = () => {
setStep('project-metadata'); setStep('project-metadata');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
const [ const [addProjectProposal, { loading: addProjectProposalLoading }] =
addProjectProposal, useMutation<
{ loading: addProjectProposalLoading },
] = useMutation<
AddProjectProposalMutation, AddProjectProposalMutation,
AddProjectProposalMutationVariables AddProjectProposalMutationVariables
>(ADD_PROJECT_PROPOSAL, { >(ADD_PROJECT_PROPOSAL, {
onCompleted({ addProjectProposal: proposalData }) { onCompleted({ addProjectProposal: proposalData }) {
history.push(`/project/${proposalData.id}`); navigate(`/project/${proposalData.id}`);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0].extensions?.info); setError(graphQLErrors[0].extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -226,7 +220,7 @@ const AddProject = () => {
AddProjectMutationVariables AddProjectMutationVariables
>(ADD_PROJECT, { >(ADD_PROJECT, {
onCompleted({ addProject: projectData }) { onCompleted({ addProject: projectData }) {
if (role === 'client') history.push(`/project/${projectData.id}`); if (role === 'client') navigate(`/project/${projectData.id}`);
else { else {
addProjectProposal({ addProjectProposal({
variables: { variables: {
@@ -242,7 +236,7 @@ const AddProject = () => {
} }
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0].extensions?.info); setError(graphQLErrors[0].extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -641,7 +635,7 @@ const AddProject = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -756,7 +750,7 @@ const AddProject = () => {
basicInfoForm.setFieldValue('imageSource', ''); basicInfoForm.setFieldValue('imageSource', '');
const data = await ( const data = await (
await fetch(`${process.env.REACT_APP_CLOUDINARY_URL}`, { await fetch(`${import.meta.env.VITE_CLOUDINARY_URL}`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}) })
@@ -1055,8 +1049,9 @@ const AddProject = () => {
{ {
...deliverablesPlatformsForm.values ...deliverablesPlatformsForm.values
.selectedDeliverables, .selectedDeliverables,
specification: !deliverablesPlatformsForm.values specification:
.selectedDeliverables.specification, !deliverablesPlatformsForm.values.selectedDeliverables
.specification,
} }
); );
}} }}
@@ -1087,8 +1082,9 @@ const AddProject = () => {
{ {
...deliverablesPlatformsForm.values ...deliverablesPlatformsForm.values
.selectedDeliverables, .selectedDeliverables,
design: !deliverablesPlatformsForm.values design:
.selectedDeliverables.design, !deliverablesPlatformsForm.values.selectedDeliverables
.design,
} }
); );
}} }}
@@ -1149,8 +1145,9 @@ const AddProject = () => {
{ {
...deliverablesPlatformsForm.values ...deliverablesPlatformsForm.values
.selectedDeliverables, .selectedDeliverables,
fullBuild: !deliverablesPlatformsForm.values fullBuild:
.selectedDeliverables.fullBuild, !deliverablesPlatformsForm.values.selectedDeliverables
.fullBuild,
} }
); );
}} }}
+10 -11
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory } from 'react-router'; import { Navigate, useNavigate } from 'react-router';
import { import {
useLazyQuery, useLazyQuery,
useMutation, useMutation,
@@ -38,7 +38,7 @@ import { GET_ALL_CATEGORIES } from '../../graphql/category.api';
import { GET_ALL_FEATURES } from '../../graphql/feature.api'; import { GET_ALL_FEATURES } from '../../graphql/feature.api';
const AddTemplate = () => { const AddTemplate = () => {
const history = useHistory(); const navigate = useNavigate();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const [newTemplate, setNewTemplate] = useState<TemplateInput>({ const [newTemplate, setNewTemplate] = useState<TemplateInput>({
name: '', name: '',
@@ -77,9 +77,8 @@ const AddTemplate = () => {
features: [], features: [],
}); });
const [availableFeatures, setAvailableFeatures] = useState< const [availableFeatures, setAvailableFeatures] =
Array<FeatureOutput> useState<Array<FeatureOutput>>();
>();
const [selectedSection, setSelectedSection] = useState< const [selectedSection, setSelectedSection] = useState<
'general' | 'specification' | 'features' 'general' | 'specification' | 'features'
@@ -108,10 +107,10 @@ const AddTemplate = () => {
AddTemplateMutationVariables AddTemplateMutationVariables
>(ADD_TEMPLATE, { >(ADD_TEMPLATE, {
onCompleted({ addTemplate: { id } }) { onCompleted({ addTemplate: { id } }) {
history.push(`/template/${id}`); navigate(`/template/${id}`);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -275,7 +274,7 @@ const AddTemplate = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -368,7 +367,7 @@ const AddTemplate = () => {
const data = await ( const data = await (
await fetch( await fetch(
`${process.env.REACT_APP_CLOUDINARY_URL}`, `${import.meta.env.VITE_CLOUDINARY_URL}`,
{ {
method: 'POST', method: 'POST',
body: formData, body: formData,
@@ -814,9 +813,9 @@ const AddTemplate = () => {
</Wrapper> </Wrapper>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'developer' && <Redirect to='/project' />)} (role === 'developer' && <Navigate to='/project' />)}
</> </>
); );
}; };
+4 -4
View File
@@ -1,7 +1,7 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useState } from 'react'; import { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useMutation, useQuery, useReactiveVar } from '@apollo/client'; import { useMutation, useQuery, useReactiveVar } from '@apollo/client';
import { import {
Box, Box,
@@ -24,7 +24,7 @@ import { GET_COUNTRY_CODES, UPDATE_USER_INFO } from '../../../graphql/auth.api';
import { userVar } from '../../../graphql/state'; import { userVar } from '../../../graphql/state';
const AdditionalInfo = () => { const AdditionalInfo = () => {
const history = useHistory(); const navigate = useNavigate();
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const currentUser = useReactiveVar(userVar); const currentUser = useReactiveVar(userVar);
const { data: countryCodes, loading: countryCodesLoading } = useQuery< const { data: countryCodes, loading: countryCodesLoading } = useQuery<
@@ -38,10 +38,10 @@ const AdditionalInfo = () => {
>(UPDATE_USER_INFO, { >(UPDATE_USER_INFO, {
onCompleted({ updateUserInfo: user }) { onCompleted({ updateUserInfo: user }) {
userVar(user); userVar(user);
history.push('/'); navigate('/');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
+1 -1
View File
@@ -25,7 +25,7 @@ const ForgotPassword = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
+4 -4
View File
@@ -1,7 +1,7 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { useState } from 'react'; import { useState } from 'react';
import { tokenVar, roleVar, userVar } from '../../../graphql/state'; import { tokenVar, roleVar, userVar } from '../../../graphql/state';
import { Login as LoginIllustration, Logo } from '../../../assets'; import { Login as LoginIllustration, Logo } from '../../../assets';
@@ -12,7 +12,7 @@ import { LOGIN } from '../../../graphql/auth.api';
import { LoginMutation, LoginMutationVariables } from '../../../graphql/types'; import { LoginMutation, LoginMutationVariables } from '../../../graphql/types';
const Login = () => { const Login = () => {
const history = useHistory(); const navigate = useNavigate();
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [login, { loading }] = useMutation< const [login, { loading }] = useMutation<
@@ -39,10 +39,10 @@ const Login = () => {
tokenVar(token); tokenVar(token);
userVar(user); userVar(user);
localStorage.setItem('token', token); localStorage.setItem('token', token);
history.push('/'); navigate('/');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
+4 -4
View File
@@ -2,7 +2,7 @@ import * as Yup from 'yup';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useState } from 'react'; import { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Login as LoginIllustration, Logo } from '../../../assets'; import { Login as LoginIllustration, Logo } from '../../../assets';
import { Box, Button, Input, Text, Alert } from '../../../components'; import { Box, Button, Input, Text, Alert } from '../../../components';
import { CONFIRM_USER_RESET_PASSWORD } from '../../../graphql/auth.api'; import { CONFIRM_USER_RESET_PASSWORD } from '../../../graphql/auth.api';
@@ -15,7 +15,7 @@ import { Wrapper } from './styles';
const RecoverAccount = () => { const RecoverAccount = () => {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const history = useHistory(); const navigate = useNavigate();
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [confirmResetPassword, { loading }] = useMutation< const [confirmResetPassword, { loading }] = useMutation<
@@ -23,10 +23,10 @@ const RecoverAccount = () => {
ConfirmUserResetPasswordMutationVariables ConfirmUserResetPasswordMutationVariables
>(CONFIRM_USER_RESET_PASSWORD, { >(CONFIRM_USER_RESET_PASSWORD, {
onCompleted() { onCompleted() {
history.push('/login'); navigate('/login');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
+4 -4
View File
@@ -1,7 +1,7 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useState } from 'react'; import { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import { Signup as SignupIllustration, Logo } from '../../../assets'; import { Signup as SignupIllustration, Logo } from '../../../assets';
import { Box, Button, Input, Link, Text, Alert } from '../../../components'; import { Box, Button, Input, Link, Text, Alert } from '../../../components';
@@ -15,7 +15,7 @@ import { SIGNUP } from '../../../graphql/auth.api';
import { roleVar, tokenVar, userVar } from '../../../graphql/state'; import { roleVar, tokenVar, userVar } from '../../../graphql/state';
const Signup = () => { const Signup = () => {
const history = useHistory(); const navigate = useNavigate();
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [signup, { loading }] = useMutation< const [signup, { loading }] = useMutation<
@@ -27,10 +27,10 @@ const Signup = () => {
tokenVar(token); tokenVar(token);
userVar(user); userVar(user);
localStorage.setItem('token', token); localStorage.setItem('token', token);
history.push('/additional-info'); navigate('/additional-info');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
+5 -5
View File
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useLazyQuery, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useReactiveVar } from '@apollo/client';
import { Redirect, useHistory, useParams } from 'react-router'; import { Navigate, useNavigate, useParams } from 'react-router';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Empty, Settings } from '../../assets'; import { Empty, Settings } from '../../assets';
import { Box, Button, Spinner, Text } from '../../components'; import { Box, Button, Spinner, Text } from '../../components';
@@ -19,7 +19,7 @@ import {
const Category = () => { const Category = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const navigate = useNavigate();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const [category, setCategory] = useState<CategoryOutput>(); const [category, setCategory] = useState<CategoryOutput>();
@@ -77,7 +77,7 @@ const Category = () => {
text='Settings' text='Settings'
iconLeft={<Settings />} iconLeft={<Settings />}
onClick={() => onClick={() =>
history.push(`/category-settings/${id || category.id}`) navigate(`/category-settings/${id || category.id}`)
} }
/> />
</Box> </Box>
@@ -109,9 +109,9 @@ const Category = () => {
</> </>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'productOwner' && <Redirect to='/project' />)} (role === 'productOwner' && <Navigate to='/project' />)}
</> </>
); );
}; };
+14 -15
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory, useParams } from 'react-router'; import { Navigate, useNavigate, useParams } from 'react-router';
import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
@@ -33,7 +33,7 @@ import {
} from '../../graphql/category.api'; } from '../../graphql/category.api';
const CategorySettings = () => { const CategorySettings = () => {
const history = useHistory(); const navigate = useNavigate();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
@@ -41,9 +41,8 @@ const CategorySettings = () => {
const [success, setSuccess] = useState<boolean>(false); const [success, setSuccess] = useState<boolean>(false);
const [category, setCategory] = useState<CategoryOutput>(); const [category, setCategory] = useState<CategoryOutput>();
const [deleteCategoryModal, setDeleteCategoryModal] = useState<boolean>( const [deleteCategoryModal, setDeleteCategoryModal] =
false useState<boolean>(false);
);
const [getCategory, { loading: categoryLoading }] = useLazyQuery< const [getCategory, { loading: categoryLoading }] = useLazyQuery<
GetCategoryByIdQuery, GetCategoryByIdQuery,
@@ -65,7 +64,7 @@ const CategorySettings = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -75,16 +74,16 @@ const CategorySettings = () => {
DeleteCategoryMutationVariables DeleteCategoryMutationVariables
>(DELETE_CATEGORY, { >(DELETE_CATEGORY, {
onCompleted() { onCompleted() {
history.push('/category'); navigate('/category');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
useEffect(() => { useEffect(() => {
getCategory({ variables: { id } }); getCategory({ variables: { id: id as string } });
// eslint-disable-next-line // eslint-disable-next-line
}, [id]); }, [id]);
@@ -105,7 +104,7 @@ const CategorySettings = () => {
onSubmit: ({ name, description, imageName, imageSource }) => { onSubmit: ({ name, description, imageName, imageSource }) => {
updateCategory({ updateCategory({
variables: { variables: {
id, id: id as string,
category: { category: {
name, name,
description, description,
@@ -126,7 +125,7 @@ const CategorySettings = () => {
description=' description='
If you delete this category you cannot recover it.' If you delete this category you cannot recover it.'
onClose={() => setDeleteCategoryModal(false)} onClose={() => setDeleteCategoryModal(false)}
onConfirm={() => deleteCategory({ variables: { id } })} onConfirm={() => deleteCategory({ variables: { id: id as string } })}
></Modal> ></Modal>
)} )}
<Box> <Box>
@@ -134,7 +133,7 @@ const CategorySettings = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -212,7 +211,7 @@ const CategorySettings = () => {
form.setFieldValue('imageSource', ''); form.setFieldValue('imageSource', '');
const data = await ( const data = await (
await fetch(`${process.env.REACT_APP_CLOUDINARY_URL}`, { await fetch(`${import.meta.env.VITE_CLOUDINARY_URL}`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}) })
@@ -273,9 +272,9 @@ const CategorySettings = () => {
</Wrapper> </Wrapper>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'productOwner' && <Redirect to='/project' />)} (role === 'productOwner' && <Navigate to='/project' />)}
</> </>
); );
}; };
+7 -7
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory, useParams } from 'react-router'; import { Navigate, useNavigate, useParams } from 'react-router';
import { useMutation, useQuery, useReactiveVar } from '@apollo/client'; import { useMutation, useQuery, useReactiveVar } from '@apollo/client';
import { useState } from 'react'; import { useState } from 'react';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
@@ -26,7 +26,7 @@ import { GET_COUNTRY_CODES } from '../../graphql/auth.api';
import { CREATE_USER, GET_ALL_USERS } from '../../graphql/admin.api'; import { CREATE_USER, GET_ALL_USERS } from '../../graphql/admin.api';
const CreateUser = () => { const CreateUser = () => {
const history = useHistory(); const navigate = useNavigate();
const { role: newUserRole } = useParams<{ const { role: newUserRole } = useParams<{
role: 'Client' | 'ProductOwner' | 'Developer'; role: 'Client' | 'ProductOwner' | 'Developer';
}>(); }>();
@@ -62,7 +62,7 @@ const CreateUser = () => {
country: '', country: '',
zip: '', zip: '',
}, },
role: newUserRole, role: newUserRole!,
}); });
const [selectedSection, setSelectedSection] = useState< const [selectedSection, setSelectedSection] = useState<
@@ -81,10 +81,10 @@ const CreateUser = () => {
: newUserRole === 'ProductOwner' : newUserRole === 'ProductOwner'
? '/product-owners' ? '/product-owners'
: '/developers'; : '/developers';
history.push(location); navigate(location);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
refetchQueries: [{ query: GET_ALL_USERS }], refetchQueries: [{ query: GET_ALL_USERS }],
@@ -179,7 +179,7 @@ const CreateUser = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -471,7 +471,7 @@ const CreateUser = () => {
</Box> </Box>
</Wrapper> </Wrapper>
) : ( ) : (
<Redirect to='/' /> <Navigate to='/' />
); );
}; };
+6 -6
View File
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { useLazyQuery, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useReactiveVar } from '@apollo/client';
import { Redirect } from 'react-router'; import { Navigate } from 'react-router';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Empty, Settings } from '../../assets'; import { Empty, Settings } from '../../assets';
import { import {
@@ -24,7 +24,7 @@ import { GET_ALL_FEATURES, GET_FEATURE_BY_ID } from '../../graphql/feature.api';
const Feature = () => { const Feature = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const navigate = useNavigate();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const [feature, setFeature] = useState<FeatureOutput>(); const [feature, setFeature] = useState<FeatureOutput>();
@@ -82,7 +82,7 @@ const Feature = () => {
text='Settings' text='Settings'
iconLeft={<Settings />} iconLeft={<Settings />}
onClick={() => onClick={() =>
history.push(`/feature-settings/${id || feature.id}`) navigate(`/feature-settings/${id || feature.id}`)
} }
/> />
</Box> </Box>
@@ -176,9 +176,9 @@ const Feature = () => {
</> </>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'productOwner' && <Redirect to='/project' />)} (role === 'productOwner' && <Navigate to='/project' />)}
</> </>
); );
}; };
+17 -15
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory, useParams } from 'react-router'; import { Navigate, useNavigate, useParams } from 'react-router';
import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
@@ -35,7 +35,7 @@ import {
} from '../../graphql/feature.api'; } from '../../graphql/feature.api';
const FeatureSettings = () => { const FeatureSettings = () => {
const history = useHistory(); const navigate = useNavigate();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
@@ -45,7 +45,7 @@ const FeatureSettings = () => {
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [success, setSuccess] = useState<boolean>(false); const [success, setSuccess] = useState<boolean>(false);
const [feature, setFeature] = useState<FeatureOutput>({ const [feature, setFeature] = useState<FeatureOutput>({
id, id: id as string,
name: '', name: '',
description: '', description: '',
featureType: '', featureType: '',
@@ -79,7 +79,7 @@ const FeatureSettings = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -89,16 +89,16 @@ const FeatureSettings = () => {
DeleteFeatureMutationVariables DeleteFeatureMutationVariables
>(DELETE_FEATURE, { >(DELETE_FEATURE, {
onCompleted() { onCompleted() {
history.push('/feature'); navigate('/feature');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
useEffect(() => { useEffect(() => {
getFeature({ variables: { id } }); getFeature({ variables: { id: id as string } });
// eslint-disable-next-line // eslint-disable-next-line
}, [id]); }, [id]);
@@ -134,7 +134,7 @@ const FeatureSettings = () => {
}) => { }) => {
updateFeature({ updateFeature({
variables: { variables: {
id, id: id as string,
feature: { feature: {
name, name,
description, description,
@@ -162,7 +162,7 @@ const FeatureSettings = () => {
onSubmit: ({ wireframes }) => { onSubmit: ({ wireframes }) => {
updateFeature({ updateFeature({
variables: { variables: {
id, id: id as string,
feature: { feature: {
name: feature.name, name: feature.name,
description: feature.description, description: feature.description,
@@ -185,7 +185,7 @@ const FeatureSettings = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -247,7 +247,9 @@ const FeatureSettings = () => {
description=' description='
If you delete this feature you cannot recover it.' If you delete this feature you cannot recover it.'
onClose={() => setDeleteFeatureModal(false)} onClose={() => setDeleteFeatureModal(false)}
onConfirm={() => deleteFeature({ variables: { id } })} onConfirm={() =>
deleteFeature({ variables: { id: id as string } })
}
></Modal> ></Modal>
)} )}
<form onSubmit={generalForm.handleSubmit}> <form onSubmit={generalForm.handleSubmit}>
@@ -287,7 +289,7 @@ const FeatureSettings = () => {
const data = await ( const data = await (
await fetch( await fetch(
`${process.env.REACT_APP_CLOUDINARY_URL}`, `${import.meta.env.VITE_CLOUDINARY_URL}`,
{ {
method: 'POST', method: 'POST',
body: formData, body: formData,
@@ -515,7 +517,7 @@ const FeatureSettings = () => {
formData.append('upload_preset', 'xofll5kc'); formData.append('upload_preset', 'xofll5kc');
const data = await ( const data = await (
await fetch(`${process.env.REACT_APP_CLOUDINARY_URL}`, { await fetch(`${import.meta.env.VITE_CLOUDINARY_URL}`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}) })
@@ -564,9 +566,9 @@ const FeatureSettings = () => {
</Wrapper> </Wrapper>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'productOwner' && <Redirect to='/project' />)} (role === 'productOwner' && <Navigate to='/project' />)}
</> </>
); );
}; };
+9 -9
View File
@@ -1,9 +1,9 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useHistory, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useLazyQuery, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useReactiveVar } from '@apollo/client';
import { Redirect } from 'react-router'; import { Navigate } from 'react-router';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Wrapper } from './styles'; import { Wrapper } from './styles';
import { Alert, Box, Button, Input, Spinner, Text } from '../../components'; import { Alert, Box, Button, Input, Spinner, Text } from '../../components';
@@ -34,7 +34,7 @@ type TransactionData = {
const Payments = () => { const Payments = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const navigate = useNavigate();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const [project, setProject] = useState<ProjectOutput>(); const [project, setProject] = useState<ProjectOutput>();
const [success, setSuccess] = useState<boolean>(false); const [success, setSuccess] = useState<boolean>(false);
@@ -60,7 +60,7 @@ const Payments = () => {
try { try {
const transactionsResult = await ( const transactionsResult = await (
await fetch(`${process.env.REACT_APP_PAYMENT_API}/transactions`, { await fetch(`${import.meta.env.VITE_PAYMENT_API}/transactions`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -128,7 +128,7 @@ const Payments = () => {
} }
const transactionsResult = await ( const transactionsResult = await (
await fetch(`${process.env.REACT_APP_PAYMENT_API}/charge`, { await fetch(`${import.meta.env.VITE_PAYMENT_API}/charge`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -158,7 +158,7 @@ const Payments = () => {
} }
} catch (err) { } catch (err) {
setPaymentLoading(false); setPaymentLoading(false);
setError(err); setError(err as string);
setSelectedChunk(undefined); setSelectedChunk(undefined);
resetForm(); resetForm();
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
@@ -182,7 +182,7 @@ const Payments = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -494,9 +494,9 @@ const Payments = () => {
</> </>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'developer' || {role === 'developer' ||
(role === 'productOwner' && <Redirect to='/project' />)} (role === 'productOwner' && <Navigate to='/project' />)}
</> </>
); );
}; };
+20 -25
View File
@@ -1,10 +1,10 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useReactToPrint } from 'react-to-print'; import { useReactToPrint } from 'react-to-print';
import { useHistory, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client';
import { Redirect } from 'react-router'; import { Navigate } from 'react-router';
import { roleVar, userVar } from '../../graphql/state'; import { roleVar, userVar } from '../../graphql/state';
import { import {
Design, Design,
@@ -80,7 +80,7 @@ type TransactionData = {
const Project = () => { const Project = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const currentUser = useReactiveVar(userVar); const currentUser = useReactiveVar(userVar);
const history = useHistory(); const navigate = useNavigate();
const printRef = useRef<HTMLDivElement>(null); const printRef = useRef<HTMLDivElement>(null);
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const [project, setProject] = useState<ProjectOutput>(); const [project, setProject] = useState<ProjectOutput>();
@@ -91,10 +91,8 @@ const Project = () => {
const [fullBuildModal, setFullBuildModal] = useState<boolean>(false); const [fullBuildModal, setFullBuildModal] = useState<boolean>(false);
const [transactionsData, setTransactionsData] = useState<TransactionData>(); const [transactionsData, setTransactionsData] = useState<TransactionData>();
const [ const [getProjectsByClientId, { loading: clientProjectsLoading }] =
getProjectsByClientId, useLazyQuery<
{ loading: clientProjectsLoading },
] = useLazyQuery<
GetAllProjectsByClientIdQuery, GetAllProjectsByClientIdQuery,
GetAllProjectsByClientIdQueryVariables GetAllProjectsByClientIdQueryVariables
>(GET_ALL_PROJECTS_BY_CLIENT_ID, { >(GET_ALL_PROJECTS_BY_CLIENT_ID, {
@@ -103,7 +101,7 @@ const Project = () => {
}, },
onCompleted({ getAllProjectsByClientId }) { onCompleted({ getAllProjectsByClientId }) {
if (getAllProjectsByClientId.length > 0) if (getAllProjectsByClientId.length > 0)
history.push(`/project/${getAllProjectsByClientId[0].id}`); navigate(`/project/${getAllProjectsByClientId[0].id}`);
}, },
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}); });
@@ -114,7 +112,7 @@ const Project = () => {
>(GET_ALL_PROJECTS, { >(GET_ALL_PROJECTS, {
onCompleted({ getAllProjects }) { onCompleted({ getAllProjects }) {
if (getAllProjects.length > 0) if (getAllProjects.length > 0)
history.push(`/project/${getAllProjects[0].id}`); navigate(`/project/${getAllProjects[0].id}`);
}, },
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}); });
@@ -146,7 +144,7 @@ const Project = () => {
setProject(changedStateProject); setProject(changedStateProject);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0].extensions?.info); setError(graphQLErrors[0].extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -161,7 +159,7 @@ const Project = () => {
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setDesignModal(false); setDesignModal(false);
setError(graphQLErrors[0].extensions?.info); setError(graphQLErrors[0].extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -176,7 +174,7 @@ const Project = () => {
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setMvpModal(false); setMvpModal(false);
setError(graphQLErrors[0].extensions?.info); setError(graphQLErrors[0].extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -191,7 +189,7 @@ const Project = () => {
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setFullBuildModal(false); setFullBuildModal(false);
setError(graphQLErrors[0].extensions?.info); setError(graphQLErrors[0].extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -225,7 +223,7 @@ const Project = () => {
try { try {
const transactionsResult = await ( const transactionsResult = await (
await fetch(`${process.env.REACT_APP_PAYMENT_API}/transactions`, { await fetch(`${import.meta.env.VITE_PAYMENT_API}/transactions`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -336,7 +334,7 @@ const Project = () => {
addDesignForm.setFieldValue('fileSource', ''); addDesignForm.setFieldValue('fileSource', '');
const data = await ( const data = await (
await fetch(`${process.env.REACT_APP_CLOUDINARY_URL}`, { await fetch(`${import.meta.env.VITE_CLOUDINARY_URL}`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}) })
@@ -384,7 +382,7 @@ const Project = () => {
addMvpForm.setFieldValue('fileSource', ''); addMvpForm.setFieldValue('fileSource', '');
const data = await ( const data = await (
await fetch(`${process.env.REACT_APP_CLOUDINARY_URL}`, { await fetch(`${import.meta.env.VITE_CLOUDINARY_URL}`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}) })
@@ -462,7 +460,7 @@ const Project = () => {
iconLeft={<Design />} iconLeft={<Design />}
disabled={!prototype} disabled={!prototype}
onClick={() => onClick={() =>
history.push(`/prototype/${project.template.id}`) navigate(`/prototype/${project.template.id}`)
} }
/> />
</Box> </Box>
@@ -474,9 +472,7 @@ const Project = () => {
text='Payments' text='Payments'
iconLeft={<Payment />} iconLeft={<Payment />}
disabled={transactionsData?.status} disabled={transactionsData?.status}
onClick={() => onClick={() => navigate(`/payments/${project.id}`)}
history.push(`/payments/${project.id}`)
}
/> />
</Box> </Box>
)} )}
@@ -487,9 +483,7 @@ const Project = () => {
variant='primary-action' variant='primary-action'
text='Settings' text='Settings'
iconLeft={<Settings />} iconLeft={<Settings />}
onClick={() => onClick={() => navigate(`/project-settings/${id}`)}
history.push(`/project-settings/${id}`)
}
/> />
</Box> </Box>
)} )}
@@ -873,7 +867,8 @@ const Project = () => {
</Box> </Box>
</Box> </Box>
)} )}
{project.template.specification && project.template.features && ( {project.template.specification &&
project.template.features && (
<Box display='none'> <Box display='none'>
<SpecificationPrint <SpecificationPrint
ref={printRef} ref={printRef}
@@ -905,7 +900,7 @@ const Project = () => {
)} )}
</> </>
) : ( ) : (
<Redirect to='/clients' /> <Navigate to='/clients' />
); );
}; };
+50 -66
View File
@@ -1,19 +1,24 @@
import { useEffect, useState, useRef, useCallback, useMemo } from 'react';
import ReactFlow, { import ReactFlow, {
removeElements,
addEdge, addEdge,
MiniMap, MiniMap,
Controls, Controls,
ControlButton, ControlButton,
FlowElement,
Elements,
Connection, Connection,
Edge, Edge,
ArrowHeadType, Node,
} from 'react-flow-renderer'; MarkerType,
import { useEffect, useState, useRef } from 'react'; applyNodeChanges,
import { useHistory, useParams } from 'react-router-dom'; applyEdgeChanges,
NodeChange,
EdgeChange,
useEdgesState,
useNodesState
} from 'reactflow';
import 'reactflow/dist/style.css';
import { useNavigate, useParams } from 'react-router-dom';
import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client';
import { Redirect } from 'react-router'; import { Navigate } from 'react-router';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Empty, ArrowLeft, Edit, CheckCircle } from '../../assets'; import { Empty, ArrowLeft, Edit, CheckCircle } from '../../assets';
import { import {
@@ -29,6 +34,7 @@ import { Wrapper } from './styles';
import { import {
AddPrototypeMutation, AddPrototypeMutation,
AddPrototypeMutationVariables, AddPrototypeMutationVariables,
FeatureOutput,
GetPrototypeByIdQuery, GetPrototypeByIdQuery,
GetPrototypeByIdQueryVariables, GetPrototypeByIdQueryVariables,
GetTemplateByIdQuery, GetTemplateByIdQuery,
@@ -47,11 +53,13 @@ import {
const Prototype = () => { const Prototype = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const navigate = useNavigate();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const nodeTypes = useMemo(() => ({ featureCard: FrontendFeatureCard }), []);
const [template, setTemplate] = useState<TemplateOutput>(); const [template, setTemplate] = useState<TemplateOutput>();
const [prototype, setPrototype] = useState<Array<ProtoTypeOutput>>(); const [prototype, setPrototype] = useState<Array<ProtoTypeOutput>>();
const [elements, setElements] = useState<Elements>([]); const [nodes, setNodes, onNodesChange] = useNodesState<FeatureOutput>([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const [editing, setEditing] = useState<boolean>(false); const [editing, setEditing] = useState<boolean>(false);
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [success, setSuccess] = useState<boolean>(false); const [success, setSuccess] = useState<boolean>(false);
@@ -86,7 +94,7 @@ const Prototype = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -107,91 +115,66 @@ const Prototype = () => {
getTemplate({ variables: { id } }); getTemplate({ variables: { id } });
getPrototype({ variables: { id } }); getPrototype({ variables: { id } });
} }
// eslint-disable-next-line
}, [id]); }, [id]);
useEffect(() => { useEffect(() => {
if (template && template.features) { if (template && template.features) {
const initialElements = template.features.map((feature, index) => { const initialNodes = template?.features?.map((feature, index) => ({
if (['frontend', 'fullstack'].includes(feature.featureType)) {
return {
id: feature.id, id: feature.id,
type: 'default', type: 'featureCard',
data: { data: feature,
label: <FrontendFeatureCard feature={feature} />,
},
position: { x: index * 100, y: index * 200 }, position: { x: index * 100, y: index * 200 },
style: { style: {
width: 'auto', width: 'auto',
}, },
connectable: role === 'developer' && editing, connectable: role === 'developer' && editing
} as FlowElement; }));
}
return {} as FlowElement;
});
if (initialElements) setElements(initialElements); if (initialNodes) setNodes(initialNodes);
} }
if (prototype) { if (prototype) {
const initialElements: Array<Edge> = []; const initialEdges: Array<Edge> = [];
prototype.forEach((link) => { prototype.forEach((link) => {
link.connections.forEach((connection) => { link.connections.forEach((connection) => {
initialElements.push({ initialEdges.push({
id: `edge-${link.feature.id}`, id: `edge-${link.feature.id}`,
source: link.feature.id, source: link.feature.id,
target: connection.to, target: connection.to,
arrowHeadType: ArrowHeadType.ArrowClosed, markerEnd: MarkerType.Arrow,
className: 'normal-edge', className: 'normal-edge'
}); });
}); });
}); });
if (initialElements) setElements((els) => [...els, ...initialElements]); if (initialEdges) setEdges(initialEdges);
} }
// eslint-disable-next-line
}, [template, prototype, editing]); }, [template, prototype, editing]);
const onElementsRemove = (elementsToRemove: Elements<any>) => const onConnect = useCallback(
setElements((els) => removeElements(elementsToRemove, els)); (params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
const onConnect = (params: Edge<any> | Connection) => [setEdges]
setElements((els) =>
addEdge({ ...params, arrowHeadType: ArrowHeadType.ArrowClosed }, els)
); );
const handleEditPrototype = () => { const handleEditPrototype = () => {
if (editing) { if (editing) {
const prototypeInput = elements const prototypeInput = edges
// @ts-ignore .map((edge) => ({
.filter((element) => element.source || element.target) featureId: edge.source,
.map((element) => {
if (
element.hasOwnProperty('source') ||
element.hasOwnProperty('target')
) {
return {
// @ts-ignore
featureId: element.source,
connections: [ connections: [
{ {
// @ts-ignore to: edge.target,
to: element.target,
releations: { back: false, forword: true }, releations: { back: false, forword: true },
}, },
], ],
}; }));
}
return {};
});
if (prototypeInput && prototypeInput.length > 0) { if (prototypeInput && prototypeInput.length > 0) {
if (prototype) { if (prototype) {
updatePrototype({ updatePrototype({
variables: { variables: {
prototype: { prototype: {
templateId: id, templateId: id as string,
// @ts-ignore
prototype: prototypeInput, prototype: prototypeInput,
}, },
}, },
@@ -200,8 +183,7 @@ const Prototype = () => {
addPrototype({ addPrototype({
variables: { variables: {
prototype: { prototype: {
templateId: id, templateId: id as string,
// @ts-ignore
prototype: prototypeInput, prototype: prototypeInput,
}, },
}, },
@@ -235,7 +217,7 @@ const Prototype = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -283,18 +265,20 @@ const Prototype = () => {
height='auto' height='auto'
> >
<ReactFlow <ReactFlow
elements={elements} fitView
onElementsRemove={onElementsRemove} nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect} onConnect={onConnect}
deleteKeyCode={46}
edgeTypes={{ arrowHeadType: 'arrow' }}
> >
{role === 'developer' && ( {role === 'developer' && (
<> <>
<MiniMap /> <MiniMap />
<Controls <Controls
showInteractive={false} showInteractive={false}
showFitView={false} showFitView
> >
<ControlButton onClick={handleEditPrototype}> <ControlButton onClick={handleEditPrototype}>
{!editing ? <Edit /> : <CheckCircle />} {!editing ? <Edit /> : <CheckCircle />}
@@ -364,7 +348,7 @@ const Prototype = () => {
)} )}
</> </>
) : ( ) : (
<>{role === 'admin' && <Redirect to='/clients' />}</> <>{role === 'admin' && <Navigate to='/clients' />}</>
); );
}; };
+7 -7
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { useMutation, useQuery, useReactiveVar } from '@apollo/client'; import { useMutation, useQuery, useReactiveVar } from '@apollo/client';
import { useState } from 'react'; import { useState } from 'react';
import { roleVar, tokenVar, userVar } from '../../graphql/state'; import { roleVar, tokenVar, userVar } from '../../graphql/state';
@@ -35,7 +35,7 @@ import {
} from '../../graphql/auth.api'; } from '../../graphql/auth.api';
const Settings = () => { const Settings = () => {
const history = useHistory(); const navigate = useNavigate();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const currentUser = useReactiveVar(userVar); const currentUser = useReactiveVar(userVar);
const { data: countryCodes, loading: countryCodesLoading } = useQuery< const { data: countryCodes, loading: countryCodesLoading } = useQuery<
@@ -68,7 +68,7 @@ const Settings = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -131,7 +131,7 @@ const Settings = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -180,11 +180,11 @@ const Settings = () => {
userVar(undefined); userVar(undefined);
roleVar(undefined); roleVar(undefined);
setDeleteAccountModal(false); setDeleteAccountModal(false);
history.push('/signup'); navigate('/signup');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setDeleteAccountModal(false); setDeleteAccountModal(false);
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -214,7 +214,7 @@ const Settings = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
+5 -5
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useParams, useHistory } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useReactiveVar } from '@apollo/client'; import { useReactiveVar } from '@apollo/client';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { roleVar, userVar } from '../../graphql/state'; import { roleVar, userVar } from '../../graphql/state';
@@ -35,7 +35,7 @@ const SupportMessaging = () => {
const { project, id } = useParams<{ id: string; project: string }>(); const { project, id } = useParams<{ id: string; project: string }>();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const currentUser = useReactiveVar(userVar); const currentUser = useReactiveVar(userVar);
const history = useHistory(); const navigate = useNavigate();
const [thread, setThread] = useState<ThreadObject>(); const [thread, setThread] = useState<ThreadObject>();
const [messages, setMessages] = useState<Array<UserMessageObject>>([]); const [messages, setMessages] = useState<Array<UserMessageObject>>([]);
const [addedMessages, setAddedMessages] = useState<Array<UserMessageObject>>( const [addedMessages, setAddedMessages] = useState<Array<UserMessageObject>>(
@@ -117,12 +117,12 @@ const SupportMessaging = () => {
>({ >({
mutation: CREATE_THREAD, mutation: CREATE_THREAD,
variables: { variables: {
projectId: project, projectId: project as string,
title, title,
threadDescription: description, threadDescription: description,
}, },
}); });
history.push( navigate(
`/support-messaging/${project}/${createdThread.data?.createThread}` `/support-messaging/${project}/${createdThread.data?.createThread}`
); );
}, },
@@ -139,7 +139,7 @@ const SupportMessaging = () => {
await clientSupport.mutate<SendMsgMutation, SendMsgMutationVariables>({ await clientSupport.mutate<SendMsgMutation, SendMsgMutationVariables>({
mutation: SEND_MSG, mutation: SEND_MSG,
variables: { variables: {
threadId: id, threadId: id as string,
username: `${currentUser?.firstName} ${currentUser?.lastName}`, username: `${currentUser?.firstName} ${currentUser?.lastName}`,
msg, msg,
}, },
+8 -10
View File
@@ -1,8 +1,8 @@
import { useReactToPrint } from 'react-to-print'; import { useReactToPrint } from 'react-to-print';
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import { useLazyQuery, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useReactiveVar } from '@apollo/client';
import { Redirect } from 'react-router'; import { Navigate } from 'react-router';
import { useHistory, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Design, Empty, Settings, Specification } from '../../assets'; import { Design, Empty, Settings, Specification } from '../../assets';
import { import {
@@ -38,7 +38,7 @@ import { GET_PROTOTYPE_BY_ID } from '../../graphql/prototype.api';
const Template = () => { const Template = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const navigate = useNavigate();
const printRef = useRef<HTMLDivElement>(null); const printRef = useRef<HTMLDivElement>(null);
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const [template, setTemplate] = useState<TemplateOutput>(); const [template, setTemplate] = useState<TemplateOutput>();
@@ -61,7 +61,7 @@ const Template = () => {
>(GET_ALL_TEMPLATES, { >(GET_ALL_TEMPLATES, {
onCompleted({ getAllTemplates }) { onCompleted({ getAllTemplates }) {
if (getAllTemplates.length > 0) if (getAllTemplates.length > 0)
history.push(`/template/${getAllTemplates[0].id}`); navigate(`/template/${getAllTemplates[0].id}`);
}, },
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}); });
@@ -150,7 +150,7 @@ const Template = () => {
prototype === undefined && role === 'productOwner' prototype === undefined && role === 'productOwner'
} }
onClick={() => onClick={() =>
history.push(`/prototype/${id || template.id}`) navigate(`/prototype/${id || template.id}`)
} }
/> />
</Box> </Box>
@@ -162,9 +162,7 @@ const Template = () => {
text='Settings' text='Settings'
iconLeft={<Settings />} iconLeft={<Settings />}
onClick={() => onClick={() =>
history.push( navigate(`/template-settings/${id || template.id}`)
`/template-settings/${id || template.id}`
)
} }
/> />
</Box> </Box>
@@ -269,8 +267,8 @@ const Template = () => {
</> </>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' && <Redirect to='/project' />} {role === 'client' && <Navigate to='/project' />}
</> </>
); );
}; };
+80 -66
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory, useParams } from 'react-router'; import { Navigate, useNavigate, useParams } from 'react-router';
import { import {
useLazyQuery, useLazyQuery,
useMutation, useMutation,
@@ -47,7 +47,7 @@ import { GET_ALL_CATEGORIES } from '../../graphql/category.api';
import { GET_ALL_FEATURES } from '../../graphql/feature.api'; import { GET_ALL_FEATURES } from '../../graphql/feature.api';
const TemplateSettings = () => { const TemplateSettings = () => {
const history = useHistory(); const navigate = useNavigate();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const [template, setTemplate] = useState<TemplateOutput>({ const [template, setTemplate] = useState<TemplateOutput>({
@@ -88,9 +88,8 @@ const TemplateSettings = () => {
features: [], features: [],
}); });
const [availableFeatures, setAvailableFeatures] = useState< const [availableFeatures, setAvailableFeatures] =
Array<FeatureOutput> useState<Array<FeatureOutput>>();
>();
const [selectedSection, setSelectedSection] = useState< const [selectedSection, setSelectedSection] = useState<
'general' | 'specification' | 'features' 'general' | 'specification' | 'features'
@@ -98,9 +97,8 @@ const TemplateSettings = () => {
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [success, setSuccess] = useState<boolean>(false); const [success, setSuccess] = useState<boolean>(false);
const [deleteTemplateModal, setDeleteTemplateModal] = useState<boolean>( const [deleteTemplateModal, setDeleteTemplateModal] =
false useState<boolean>(false);
);
const { data: categories, loading: categoriesLoading } = useQuery< const { data: categories, loading: categoriesLoading } = useQuery<
GetAllCategoriesQuery, GetAllCategoriesQuery,
@@ -139,7 +137,7 @@ const TemplateSettings = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -149,16 +147,16 @@ const TemplateSettings = () => {
DeleteTemplateMutationVariables DeleteTemplateMutationVariables
>(DELETE_TEMPLATE, { >(DELETE_TEMPLATE, {
onCompleted() { onCompleted() {
history.push('/template'); navigate('/template');
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
useEffect(() => { useEffect(() => {
getTemplate({ variables: { id } }); getTemplate({ variables: { id: id as string } });
getFeatures(); getFeatures();
// eslint-disable-next-line // eslint-disable-next-line
@@ -189,7 +187,7 @@ const TemplateSettings = () => {
}); });
updateTemplate({ updateTemplate({
variables: { variables: {
id, id: id as string,
template: { template: {
name, name,
description, description,
@@ -203,35 +201,42 @@ const TemplateSettings = () => {
specification: { specification: {
introduction: { introduction: {
purpose: template.specification?.introduction.purpose!, purpose: template.specification?.introduction.purpose!,
documentConventions: template.specification?.introduction documentConventions:
.documentConventions!, template.specification?.introduction.documentConventions!,
intendedAudience: template.specification?.introduction intendedAudience:
.intendedAudience!, template.specification?.introduction.intendedAudience!,
projectScope: template.specification?.introduction.projectScope!, projectScope: template.specification?.introduction.projectScope!,
}, },
overallDescription: { overallDescription: {
perspective: template.specification?.overallDescription perspective:
.perspective!, template.specification?.overallDescription.perspective!,
userCharacteristics: template.specification?.overallDescription userCharacteristics:
.userCharacteristics!, template.specification?.overallDescription.userCharacteristics!,
operatingEnvironment: template.specification?.overallDescription operatingEnvironment:
template.specification?.overallDescription
.operatingEnvironment!, .operatingEnvironment!,
designImplementationConstraints: template.specification designImplementationConstraints:
?.overallDescription.designImplementationConstraints!, template.specification?.overallDescription
userDocumentation: template.specification?.overallDescription .designImplementationConstraints!,
.userDocumentation!, userDocumentation:
assemptionsDependencies: template.specification template.specification?.overallDescription.userDocumentation!,
?.overallDescription.assemptionsDependencies!, assemptionsDependencies:
template.specification?.overallDescription
.assemptionsDependencies!,
}, },
nonFunctionalRequirements: { nonFunctionalRequirements: {
performanceRequirements: template.specification performanceRequirements:
?.nonFunctionalRequirements.performanceRequirements!, template.specification?.nonFunctionalRequirements
safetyRequirements: template.specification .performanceRequirements!,
?.nonFunctionalRequirements.safetyRequirements!, safetyRequirements:
securityRequirements: template.specification template.specification?.nonFunctionalRequirements
?.nonFunctionalRequirements.securityRequirements!, .safetyRequirements!,
softwareQualityAttributes: template.specification securityRequirements:
?.nonFunctionalRequirements.softwareQualityAttributes!, template.specification?.nonFunctionalRequirements
.securityRequirements!,
softwareQualityAttributes:
template.specification?.nonFunctionalRequirements
.softwareQualityAttributes!,
}, },
otherRequirements: template.specification?.otherRequirements!, otherRequirements: template.specification?.otherRequirements!,
glossary: template.specification?.glossary!, glossary: template.specification?.glossary!,
@@ -375,7 +380,7 @@ const TemplateSettings = () => {
}); });
updateTemplate({ updateTemplate({
variables: { variables: {
id, id: id as string,
template: { template: {
name: template.name, name: template.name,
description: template.description, description: template.description,
@@ -425,7 +430,7 @@ const TemplateSettings = () => {
onSubmit: ({ features }) => { onSubmit: ({ features }) => {
updateTemplate({ updateTemplate({
variables: { variables: {
id, id: id as string,
template: { template: {
name: template.name, name: template.name,
description: template.description, description: template.description,
@@ -439,35 +444,42 @@ const TemplateSettings = () => {
specification: { specification: {
introduction: { introduction: {
purpose: template.specification?.introduction.purpose!, purpose: template.specification?.introduction.purpose!,
documentConventions: template.specification?.introduction documentConventions:
.documentConventions!, template.specification?.introduction.documentConventions!,
intendedAudience: template.specification?.introduction intendedAudience:
.intendedAudience!, template.specification?.introduction.intendedAudience!,
projectScope: template.specification?.introduction.projectScope!, projectScope: template.specification?.introduction.projectScope!,
}, },
overallDescription: { overallDescription: {
perspective: template.specification?.overallDescription perspective:
.perspective!, template.specification?.overallDescription.perspective!,
userCharacteristics: template.specification?.overallDescription userCharacteristics:
.userCharacteristics!, template.specification?.overallDescription.userCharacteristics!,
operatingEnvironment: template.specification?.overallDescription operatingEnvironment:
template.specification?.overallDescription
.operatingEnvironment!, .operatingEnvironment!,
designImplementationConstraints: template.specification designImplementationConstraints:
?.overallDescription.designImplementationConstraints!, template.specification?.overallDescription
userDocumentation: template.specification?.overallDescription .designImplementationConstraints!,
.userDocumentation!, userDocumentation:
assemptionsDependencies: template.specification template.specification?.overallDescription.userDocumentation!,
?.overallDescription.assemptionsDependencies!, assemptionsDependencies:
template.specification?.overallDescription
.assemptionsDependencies!,
}, },
nonFunctionalRequirements: { nonFunctionalRequirements: {
performanceRequirements: template.specification performanceRequirements:
?.nonFunctionalRequirements.performanceRequirements!, template.specification?.nonFunctionalRequirements
safetyRequirements: template.specification .performanceRequirements!,
?.nonFunctionalRequirements.safetyRequirements!, safetyRequirements:
securityRequirements: template.specification template.specification?.nonFunctionalRequirements
?.nonFunctionalRequirements.securityRequirements!, .safetyRequirements!,
softwareQualityAttributes: template.specification securityRequirements:
?.nonFunctionalRequirements.softwareQualityAttributes!, template.specification?.nonFunctionalRequirements
.securityRequirements!,
softwareQualityAttributes:
template.specification?.nonFunctionalRequirements
.softwareQualityAttributes!,
}, },
otherRequirements: template.specification?.otherRequirements!, otherRequirements: template.specification?.otherRequirements!,
glossary: template.specification?.glossary!, glossary: template.specification?.glossary!,
@@ -487,7 +499,7 @@ const TemplateSettings = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -541,7 +553,9 @@ const TemplateSettings = () => {
description=' description='
If you delete this template you cannot recover it.' If you delete this template you cannot recover it.'
onClose={() => setDeleteTemplateModal(false)} onClose={() => setDeleteTemplateModal(false)}
onConfirm={() => deleteTemplate({ variables: { id } })} onConfirm={() =>
deleteTemplate({ variables: { id: id as string } })
}
></Modal> ></Modal>
)} )}
<Box <Box
@@ -599,7 +613,7 @@ const TemplateSettings = () => {
const data = await ( const data = await (
await fetch( await fetch(
`${process.env.REACT_APP_CLOUDINARY_URL}`, `${import.meta.env.VITE_CLOUDINARY_URL}`,
{ {
method: 'POST', method: 'POST',
body: formData, body: formData,
@@ -1064,9 +1078,9 @@ const TemplateSettings = () => {
</Wrapper> </Wrapper>
) : ( ) : (
<> <>
{role === 'admin' && <Redirect to='/clients' />} {role === 'admin' && <Navigate to='/clients' />}
{role === 'client' || {role === 'client' ||
(role === 'developer' && <Redirect to='/project' />)} (role === 'developer' && <Navigate to='/project' />)}
</> </>
); );
}; };
+7 -7
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useHistory, useParams } from 'react-router'; import { useNavigate, useParams } from 'react-router';
import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
@@ -18,7 +18,7 @@ import { ArrowLeft } from '../../assets';
import { theme } from '../../themes'; import { theme } from '../../themes';
const UpdateProject = () => { const UpdateProject = () => {
const history = useHistory(); const navigate = useNavigate();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [project, setProject] = useState<ProjectOutput>(); const [project, setProject] = useState<ProjectOutput>();
@@ -39,10 +39,10 @@ const UpdateProject = () => {
UpdateProjectMutationVariables UpdateProjectMutationVariables
>(UPDATE_PROJECT, { >(UPDATE_PROJECT, {
onCompleted() { onCompleted() {
history.push(`/project/${id}`); navigate(`/project/${id}`);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0].extensions?.info); setError(graphQLErrors[0].extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -69,7 +69,7 @@ const UpdateProject = () => {
onSubmit: ({ name, imageName, imageSource }) => { onSubmit: ({ name, imageName, imageSource }) => {
updateProject({ updateProject({
variables: { variables: {
id, id: id as string,
name, name,
image: { name: imageName, src: imageSource }, image: { name: imageName, src: imageSource },
}, },
@@ -92,7 +92,7 @@ const UpdateProject = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -157,7 +157,7 @@ const UpdateProject = () => {
basicInfoForm.setFieldValue('imageSource', ''); basicInfoForm.setFieldValue('imageSource', '');
const data = await ( const data = await (
await fetch(`${process.env.REACT_APP_CLOUDINARY_URL}`, { await fetch(`${import.meta.env.VITE_CLOUDINARY_URL}`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}) })
+7 -7
View File
@@ -1,6 +1,6 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Redirect, useHistory, useParams } from 'react-router'; import { Navigate, useNavigate, useParams } from 'react-router';
import { useMutation, useQuery, useReactiveVar } from '@apollo/client'; import { useMutation, useQuery, useReactiveVar } from '@apollo/client';
import { useState } from 'react'; import { useState } from 'react';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
@@ -35,7 +35,7 @@ import {
} from '../../graphql/auth.api'; } from '../../graphql/auth.api';
const UserSettings = () => { const UserSettings = () => {
const history = useHistory(); const navigate = useNavigate();
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const [userToEdit, setUserToEdit] = useState<UserOutput>(); const [userToEdit, setUserToEdit] = useState<UserOutput>();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
@@ -48,7 +48,7 @@ const UserSettings = () => {
GetUserByIdQueryVariables GetUserByIdQueryVariables
>(GET_USER_BY_ID, { >(GET_USER_BY_ID, {
variables: { variables: {
id, id: id as string,
}, },
onCompleted({ getUserById }) { onCompleted({ getUserById }) {
setUserToEdit(getUserById); setUserToEdit(getUserById);
@@ -79,7 +79,7 @@ const UserSettings = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -143,7 +143,7 @@ const UserSettings = () => {
setTimeout(() => setSuccess(false), 3000); setTimeout(() => setSuccess(false), 3000);
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -189,7 +189,7 @@ const UserSettings = () => {
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
size='small' size='small'
onClick={() => history.goBack()} onClick={() => navigate(-1)}
iconLeft={<ArrowLeft />} iconLeft={<ArrowLeft />}
/> />
<Text variant='headline' weight='bold'> <Text variant='headline' weight='bold'>
@@ -481,7 +481,7 @@ const UserSettings = () => {
</Box> </Box>
</Wrapper> </Wrapper>
) : ( ) : (
<Redirect to='/' /> <Navigate to='/' />
); );
}; };
+7 -9
View File
@@ -2,7 +2,7 @@ import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useMutation, useLazyQuery, useReactiveVar } from '@apollo/client'; import { useMutation, useLazyQuery, useReactiveVar } from '@apollo/client';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Redirect, useHistory, useLocation } from 'react-router'; import { Navigate, useNavigate, useLocation } from 'react-router';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Add, Delete, Edit, Empty } from '../../assets'; import { Add, Delete, Edit, Empty } from '../../assets';
import { import {
@@ -27,7 +27,7 @@ import { DELETE_USER } from '../../graphql/auth.api';
const Users = () => { const Users = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const [users, setUsers] = useState<Array<UserOutput>>(); const [users, setUsers] = useState<Array<UserOutput>>();
const [userToDelete, setUserToDelete] = useState<UserOutput>(); const [userToDelete, setUserToDelete] = useState<UserOutput>();
@@ -67,7 +67,7 @@ const Users = () => {
}, },
onError({ graphQLErrors }) { onError({ graphQLErrors }) {
setDeleteAccountModal(false); setDeleteAccountModal(false);
setError(graphQLErrors[0]?.extensions?.info); setError(graphQLErrors[0]?.extensions?.info as string);
setTimeout(() => setError(''), 3000); setTimeout(() => setError(''), 3000);
}, },
}); });
@@ -154,7 +154,7 @@ const Users = () => {
}`} }`}
iconLeft={<Add />} iconLeft={<Add />}
onClick={() => onClick={() =>
history.push( navigate(
`/create-user/${ `/create-user/${
location.pathname === '/clients' location.pathname === '/clients'
? 'Client' ? 'Client'
@@ -220,9 +220,7 @@ const Users = () => {
justifySelf='flex-end' justifySelf='flex-end'
> >
<Box <Box
onClick={() => onClick={() => navigate(`/user-settings/${user.id}`)}
history.push(`/user-settings/${user.id}`)
}
marginRight='15px' marginRight='15px'
cursor='pointer' cursor='pointer'
> >
@@ -273,7 +271,7 @@ const Users = () => {
}`} }`}
iconLeft={<Add />} iconLeft={<Add />}
onClick={() => onClick={() =>
history.push( navigate(
`/create-user/${ `/create-user/${
location.pathname === '/clients' location.pathname === '/clients'
? 'Client' ? 'Client'
@@ -304,7 +302,7 @@ const Users = () => {
)} )}
</> </>
) : ( ) : (
<Redirect to='/clients' /> <Navigate to='/clients' />
); );
}; };
+2 -1
View File
@@ -1,12 +1,13 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"types": ["node", "vite/client"],
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"module": "esnext", "module": "esnext",
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
export default defineConfig({
plugins: [svgr(), react()],
})
+2948 -11061
View File
File diff suppressed because it is too large Load Diff