Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix stale case data #768

Merged
merged 124 commits into from
Aug 3, 2023
Merged
Changes from 1 commit
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
f8654c3
chore: add eslint unused import plugin to projects seperately (#425)
AfaqShuaib09 May 29, 2023
40b9437
feat(generated assignee-asigned-guard): assign endpoint (#451)
Blokh Jun 6, 2023
e627d71
Blokh/feat/backoffice image (#505)
Blokh Jun 6, 2023
13a4924
added logo url
Blokh Jun 6, 2023
cecbc7f
fix image logo by updating name to VITE_IMAGE_LOGO_URL
Blokh Jun 6, 2023
fb72c45
refactor(db-schema): remove enduser unneccesary fields (#508)
alonp99 Jun 7, 2023
39b099b
fixed document props update
alonp99 Jun 7, 2023
969a655
backoffice: fixed addtional info on entity
alonp99 Jun 7, 2023
8d4ef04
add ajv keywords
alonp99 Jun 7, 2023
32194e2
update pnpm lock file
alonp99 Jun 7, 2023
c8762aa
Create SECURITY.md (#513)
alonp99 Jun 7, 2023
db18f37
fix pdf view in backoffice
alonp99 Jun 8, 2023
7ffca00
fix(backoffice-v2): fixed height for image viewer - addresses how pdf…
Omri-Levy Jun 8, 2023
2a651aa
Blokh/feat/default case filter assignee (#480)
Blokh Jun 8, 2023
527df40
update doc type (#515)
alonp99 Jun 8, 2023
98ce0a5
updated common reference
Blokh Jun 8, 2023
a8abf08
update common versions
alonp99 Jun 8, 2023
ca83511
fix: upgrade @astrojs/mdx from 0.18.4 to 0.19.2 (#518)
alonp99 Jun 10, 2023
24d5603
fix: upgrade @ballerine/common from 0.4.3 to 0.4.4 (#519)
alonp99 Jun 10, 2023
07d8021
update pnpm lock file
alonp99 Jun 10, 2023
09c9d42
backoffice signin logo regression fix
alonp99 Jun 10, 2023
2817ec7
added test env ci
alonp99 Jun 12, 2023
7894109
Cases pagination, sorting, filtering, and search (#516)
MatanYadaev Jun 13, 2023
78fa77a
feat: create workflows websocket service (#499)
TzlilSwimmer123 Jun 13, 2023
f366cf4
feat(vite.config.ts): added sourcemaps
Omri-Levy Jun 13, 2023
9ef1158
update common
alonp99 Jun 13, 2023
d2598bf
Blokh/fix/refactor update category (#517)
Blokh Jun 14, 2023
4b6b32a
Blokh/feat/test infra (#500)
Blokh Jun 14, 2023
06ae2d4
feat: added handling of prisma validation errors to workflow controll…
chesterkmr Jun 14, 2023
0dacb3a
add kyb w/ external request (#541)
alonp99 Jun 17, 2023
e41846c
fix(backoffice-v2): fixed document type resetting to "undefined"
Omri-Levy Jun 18, 2023
a02b886
fix(*): no longer using hardcoded propertiesSchema
Omri-Levy Jun 18, 2023
d1b9296
update common lib
alonp99 Jun 18, 2023
9810ab0
feat(workflow-def): enable mulitple active workflows
alonp99 Jun 18, 2023
8899fcd
update pnpm lock
alonp99 Jun 18, 2023
6b4dc4f
updated cookie secure to false for testing
Blokh Jun 18, 2023
52e220c
added secure proxy to cookie logic
Blokh Jun 18, 2023
b987c0d
added trust proxy key to app
Blokh Jun 18, 2023
d24467d
added trust proxy key to app
Blokh Jun 18, 2023
dd87e6e
added trust proxy key to app
Blokh Jun 18, 2023
5c3f637
reverted unnecessary changes
Blokh Jun 18, 2023
6a3f87b
reverted unnecessary changes
Blokh Jun 18, 2023
73fd998
added secureProxy
Blokh Jun 18, 2023
428aa5f
removed secured of cookie
Blokh Jun 18, 2023
af68fe9
updated removed http only
Blokh Jun 18, 2023
31bb1b9
added secured false
Blokh Jun 18, 2023
ce8648e
remove cookie domaun
alonp99 Jun 18, 2023
94b1f48
WIP - Headless example fix (#510)
Omri-Levy Jun 19, 2023
307a5aa
fix conflicts
alonp99 Jun 19, 2023
6c91250
fix(swagger): dont open swagger in dev/prod (#533)
alonp99 Jun 20, 2023
7cf1a60
add timestamps to workflow webhooks (#561)
alonp99 Jun 20, 2023
76ca3ec
fix(docs): update astro (#565)
alonp99 Jun 20, 2023
0eda114
refactor(*): GET /workflows per entity instead of query params (#558)
Omri-Levy Jun 20, 2023
90ace1c
fix(webhooks): fix resolved ata (#574)
alonp99 Jun 21, 2023
520a44d
refactor(backoffice-v2): merged re-submit and reject into a single di…
Omri-Levy Jun 21, 2023
b13e054
feat(backoffice-v2): added rotate button to selected image (#576)
Omri-Levy Jun 21, 2023
2e94009
feat(backoffice-v2): added a content block to display plugin output (…
Omri-Levy Jun 22, 2023
f6df3fd
Updated pluginOutput to pluginsOutput (#582)
Omri-Levy Jun 22, 2023
2f5be20
Backoffice - Zoom document images (#575)
Omri-Levy Jun 22, 2023
c8817d9
Backoffice - feedback updates (#583)
Omri-Levy Jun 22, 2023
6a82f2d
Backoffice - Entity address + map block (#584)
Omri-Levy Jun 22, 2023
cff7e52
Docs site sturcutre changes (#580)
alonp99 Jun 22, 2023
8343bde
Update simple-kyb-guide.mdx (#586)
nitzanballerine Jun 23, 2023
d6417e1
Tech/feat/dynamic workflows (#538)
Blokh Jun 23, 2023
06746a1
update docs
alonp99 Jun 23, 2023
cbb6b29
update docs
alonp99 Jun 23, 2023
2c8730f
adding helm, dev
alonp99 Jun 23, 2023
78d93b0
update docs
alonp99 Jun 23, 2023
27ea2a5
update docs
alonp99 Jun 23, 2023
a515cfc
workflows dashboard (#567)
alonp99 Jun 23, 2023
bb494ab
update docs
alonp99 Jun 23, 2023
f89a176
update workflows dashboard name
alonp99 Jun 23, 2023
fbf844d
fix: fixed websocket-service path in init script (#594)
chesterkmr Jun 24, 2023
0a114e1
Illia rudniev/feat/winston logger (#544)
chesterkmr Jun 26, 2023
d93e8bd
feat: updated jq with jmespath (#600)
Blokh Jun 27, 2023
d5e8458
Blokh/feat/plugins documentation (#611)
Blokh Jun 28, 2023
9b5c6c7
fix(docs): fixed docs (#612)
alonp99 Jun 28, 2023
a030e41
Illia rudniev/feat/overview page and charts (#596)
chesterkmr Jun 28, 2023
6ace54c
refactor: refactored way of aquiring entity type & removed entity fro…
chesterkmr Jun 28, 2023
4962d42
fix(docs): add plugins section (#613)
alonp99 Jun 28, 2023
08428bb
test(headless-example): added an e2e smoke test - catches instances o…
Omri-Levy Jun 28, 2023
206e604
Illia rudniev/feat/workflow dashboard auth (#602)
chesterkmr Jun 29, 2023
d20a979
blokh/feat/definition-validator (#608)
Blokh Jun 29, 2023
39cc043
feat: implemented user activity tracker middleware & tests, connected…
chesterkmr Jul 1, 2023
e95b9c5
Fix headless-exampe 401 unauthorized errors (#624)
Omri-Levy Jul 2, 2023
41f62ab
Disable ability to change assignment on a case with a decision (#622)
Omri-Levy Jul 2, 2023
cd53458
Alphanumeric doc number (#623)
Omri-Levy Jul 2, 2023
1100d59
feat(backoffice-v2): added a comment field for reject/revision reason…
Omri-Levy Jul 3, 2023
7e99d5f
fix: fixed workflow dashboard port in env.example (#634)
chesterkmr Jul 3, 2023
14ce181
Illia rudniev/feat/wf runtimes page metrics (#626)
chesterkmr Jul 4, 2023
a43667f
add staging image publish
alonp99 Jul 4, 2023
2cd89a2
exper
alonp99 Jul 4, 2023
6ab0329
fix: fixed imports & added preview port to vite config (#647)
chesterkmr Jul 6, 2023
fa979a7
fix(webhook-handler): null check on document changed (#648)
alonp99 Jul 6, 2023
b610b1f
add: github actions to build & push docker images (#664)
pratapalakshmi Jul 10, 2023
0494828
Revert "add: github actions to build & push docker images (#664)"
alonp99 Jul 10, 2023
7362047
feat(schema): schema changes for kyb (#630)
alonp99 Jul 10, 2023
9f4c4a1
refactor: metric moved to different module & renamed endpoints & fixe…
chesterkmr Jul 10, 2023
585b20a
feat(doc-schemas): add mtn statement (#672)
alonp99 Jul 11, 2023
19fc64d
keep the values up to date docker-compose.yml (#646)
pratapalakshmi Jul 12, 2023
1f0bd1c
fix: added biging serialization & updated models & FE types (#675)
chesterkmr Jul 12, 2023
aadd5fd
feat(docs): add docs schema type (#676)
alonp99 Jul 12, 2023
75b224d
fix: fixed wf processing stats queries, added missing key to user res…
chesterkmr Jul 12, 2023
0c5683c
update default context schema
alonp99 Jul 12, 2023
4023c92
fix: removed grouping from approval rate quer & review time query now…
chesterkmr Jul 13, 2023
d4e63a1
alonp/fix/default context to js (#682)
alonp99 Jul 13, 2023
1b824b7
Illia rudniev/feat/react UI lib (#666)
chesterkmr Jul 13, 2023
0cd2be2
Authentication layouts using react-router-dom (#617)
Omri-Levy Jul 16, 2023
f15a5d7
publish ui package (#692)
alonp99 Jul 17, 2023
2a3ce3f
fix(*): renamed lib to dist (#693)
Omri-Levy Jul 17, 2023
dc5d81b
publish ui package
alonp99 Jul 17, 2023
0c5693f
Illia rudniev/fix/active agents stats (#697)
chesterkmr Jul 17, 2023
d5c710a
fix(editabledetails): fixes status not updating from revision to appr…
Omri-Levy Jul 22, 2023
1fceb59
Revert "fix(editabledetails): fixes status not updating from revision…
alonp99 Jul 23, 2023
b1ccffe
lock ballerine node package
alonp99 Jul 23, 2023
2128b98
lock ballerine node package
alonp99 Jul 23, 2023
3f56d45
feat(db): add plv8 (#727)
alonp99 Jul 27, 2023
813c63d
fix(postgres): change volume to avoid conflicts with all setup
alonp99 Jul 27, 2023
839df7b
feat(documents-schema): added now doc schema (#754)
alonp99 Aug 2, 2023
34e6e38
change document type
alonp99 Aug 2, 2023
bcd20f4
fix: remove @ballerine/ui from build (#758)
tomer-shvadron Aug 2, 2023
babb1c9
fix(document schemas): fixed typo of regestration
Omri-Levy Aug 2, 2023
542c0d6
fix(gh.ts): renamed businessName to companyName, and removed alphaNum…
Omri-Levy Aug 3, 2023
a6b2d91
fix(backoffice-v2): no longer sharing data between cases (different w…
Omri-Levy Aug 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Illia rudniev/feat/workflow dashboard auth (#602)
* feat: added overview page & updated pie chart

* feat: added sorting to worklofws table & added sorting to controller

* fix: fixed sorting regexp cache bug

* feat: added signin from backoffice & session handling

* refactor: added post-fixes to query/mutation hooks & useSession now uses query refetch method

* fix: fixed typo and missing space

* fix: fix logout in header & updated dev port
chesterkmr authored Jun 29, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 206e604a34757da62478918db4ec2730349bc0cc
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ export const AssignButton: React.FC<IAssignButtonProps> = ({
<DropdownMenuContent className={`min-w-[16rem]`} align={'start'}>
{isUnassignEnabled ? (
<DropdownMenuItem
className={`border-b-2 text-cyan-950`}
className={`text-cyan-950 border-b-2`}
onClick={() => onAssigneeSelect(null)}
>
Unassign
5 changes: 4 additions & 1 deletion apps/workflows-dashboard/package.json
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
"@radix-ui/react-dialog": "^1.0.2",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.1",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-select": "^1.2.1",
"@radix-ui/react-separator": "^1.0.2",
@@ -34,12 +35,14 @@
"react": "^18.2.0",
"react-custom-scrollbars": "^4.2.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-router-dom": "^6.11.2",
"recharts": "^2.7.2",
"tailwind-merge": "^1.13.2",
"tailwindcss-animate": "^1.0.5",
"use-query-params": "^2.2.1",
"zod": "^3.21.4"
"zod": "^3.21.4",
"vite-plugin-terminal": "^1.1.0"
},
"devDependencies": {
"@types/axios": "^0.14.0",
25 changes: 25 additions & 0 deletions apps/workflows-dashboard/src/common/env/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { ZodFormattedError } from 'zod';
import { EnvSchema } from './schema';
import { terminal } from 'virtual:terminal';

export const formatErrors = (errors: ZodFormattedError<Map<string, string>, string>) => {
console.info({
errors,
});

return Object.entries(errors)
.map(([name, value]) => {
if (value && '_errors' in value) return `${name}: ${value._errors.join(', ')}\n`;
})
.filter(Boolean);
};

const _env = EnvSchema.safeParse(import.meta.env);

// TypeScript complains with !env.success
if (_env.success === false) {
terminal.error('❌ Invalid environment variables:\n', ...formatErrors(_env.error.format()));
throw new Error('Invalid environment variables');
}

export const env = _env.data;
7 changes: 7 additions & 0 deletions apps/workflows-dashboard/src/common/env/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { z } from 'zod';

export const EnvSchema = z.object({
MODE: z.enum(['development', 'production', 'test']),
VITE_API_URL: z.string().url().default('https://api-dev.ballerine.io/v2'),
VITE_IMAGE_LOGO_URL: z.string().optional(),
});
5 changes: 5 additions & 0 deletions apps/workflows-dashboard/src/common/errors/http-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class HttpError extends Error {
constructor(public code: number, public message: string) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './withSessionProtected';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function clearRefererUrl(): void {
localStorage.removeItem('_ref');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function getRefererUrl(): string | null {
return localStorage.getItem('_ref') || null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function setRefererUrl(url: string) {
localStorage.setItem('_ref', url);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { setRefererUrl } from '@app/common/hocs/withSessionProtected/utils/set-referer-url';
import { useSession } from '@app/common/hooks/useSession';
import { LoadingSpinner } from '@app/components/atoms/LoadingSpinner';
import { useLayoutEffect } from 'react';
import { Navigate } from 'react-router-dom';

export function withSessionProtected<TComponentProps extends object>(
Component: React.ComponentType<TComponentProps>,
signinPath = '/auth/signin',
): React.ComponentType<TComponentProps> {
function Wrapper(props: TComponentProps) {
const { isAuthenticated, isLoading } = useSession();

useLayoutEffect(() => {
if (!isLoading && !isAuthenticated && location.pathname !== signinPath) {
setRefererUrl(location.pathname);
}
}, [isAuthenticated, isLoading]);

if (isLoading) {
return (
<div className="flex h-full flex-col items-center justify-center">
<LoadingSpinner />
</div>
);
}

if (!isAuthenticated) return <Navigate to={signinPath} />;

return <Component {...props} />;
}
Wrapper.displayName = `withSessionProtected(${Component.displayName})`;

return Wrapper;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useLogoutMutation';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { fetchLogout } from '@app/domains/auth/api/logout';
import { sessionKeys } from '@app/domains/auth/api/session/query-keys';
import { queryClient } from '@app/lib/react-query/query-client';
import { useMutation } from '@tanstack/react-query';

export function useLogoutMutation() {
const { mutate } = useMutation({
mutationFn: fetchLogout,
onSuccess: () => queryClient.invalidateQueries(sessionKeys.details()),
});

return {
logout: mutate,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useSession';
24 changes: 24 additions & 0 deletions apps/workflows-dashboard/src/common/hooks/useSession/useSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { sessionKeys } from '@app/domains/auth/api/session/query-keys';
import { useQuery } from '@tanstack/react-query';

const ONE_MINUTE_IN_MS = 1000 * 60;

export function useSession() {
const {
data: user,
isLoading,
refetch,
} = useQuery({
...sessionKeys.details(),
refetchInterval: ONE_MINUTE_IN_MS,
});

const isAuthenticated = Boolean(!isLoading && user);

return {
isLoading,
isAuthenticated,
user,
refresh: refetch,
};
}
4 changes: 4 additions & 0 deletions apps/workflows-dashboard/src/common/utils/ctw/ctw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export const ctw = (...classNames: Array<ClassValue>) => twMerge(clsx(classNames));
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ctw } from '@app/common/utils/ctw/ctw';
import { forwardRef, HTMLAttributes } from 'react';

export const AlertDescription = forwardRef<
HTMLParagraphElement,
HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={ctw('text-sm [&_p]:leading-relaxed', className)} {...props} />
));
AlertDescription.displayName = 'AlertDescription';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ctw } from '@app/common/utils/ctw/ctw';
import { forwardRef, HTMLAttributes } from 'react';

export const AlertTitle = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLHeadingElement>>(
({ className, ...props }, ref) => (
<h5
ref={ref}
className={ctw('mb-1 font-medium leading-none tracking-tight', className)}
{...props}
/>
),
);
AlertTitle.displayName = 'AlertTitle';
27 changes: 27 additions & 0 deletions apps/workflows-dashboard/src/components/atoms/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { forwardRef, HTMLAttributes } from 'react';
import { cva, VariantProps } from 'class-variance-authority';
import { ctw } from '@app/common/utils/ctw/ctw';

const alertVariants = cva(
'relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11',
{
variants: {
variant: {
default: 'bg-background text-foreground',
destructive:
'text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive',
},
},
defaultVariants: {
variant: 'default',
},
},
);

export const Alert = forwardRef<
HTMLDivElement,
HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div ref={ref} role="alert" className={ctw(alertVariants({ variant }), className)} {...props} />
));
Alert.displayName = 'Alert';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Alert } from '../Alert/Alert';
import { AlertCircle } from 'lucide-react';
import { AlertTitle } from '../Alert/Alert.Title';
import { AlertDescription } from '../Alert/Alert.Description';

interface Props {
children: React.ReactNode;
}

export const ErrorAlert = ({ children }: Props) => {
return (
<div className={`mt-3 mb-1`}>
<Alert variant={`destructive`} className={`w-full max-w-lg`}>
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>{children}</AlertDescription>
</Alert>
</div>
);
};
16 changes: 16 additions & 0 deletions apps/workflows-dashboard/src/components/atoms/Label/Label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react';
import * as LabelPrimitive from '@radix-ui/react-label';
import { cva, VariantProps } from 'class-variance-authority';
import { ctw } from '@app/common/utils/ctw/ctw';

const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
);

export const Label = forwardRef<
ElementRef<typeof LabelPrimitive.Root>,
ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root ref={ref} className={ctw(labelVariants(), className)} {...props} />
));
Label.displayName = LabelPrimitive.Root.displayName;
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { FunctionComponent, SVGProps } from 'react';

/**
* @description The Ballerine logo SVG found in Ballerine's Figma design.
* @param props
* @constructor
*/
export const BallerineLogo: FunctionComponent<SVGProps<SVGSVGElement>> = props => {
return (
<svg
width="153"
height="34"
viewBox="0 0 153 34"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g clipPath="url(#clip0_2190_10239)">
<path
d="M15.0495 34C11.7759 34 9.24159 32.4362 7.99481 31.4763C7.16779 30.8415 7.02056 29.6617 7.66275 28.8443C8.30493 28.0268 9.49847 27.8812 10.3223 28.516C11.2151 29.2034 13.0571 30.343 15.3283 30.2439C19.1689 30.0767 22.7996 26.528 22.7996 22.9392C22.7996 21.1494 22.4644 19.9634 21.5622 18.5699L21.5278 18.5142C19.786 15.6004 16.0112 13.6402 12.1361 13.6402C11.1776 13.6402 10.2879 13.7641 9.48907 14.0118C8.48976 14.3215 7.4278 13.7703 7.11454 12.7825C6.80127 11.7947 7.35888 10.7419 8.35819 10.4353C9.52353 10.0761 10.7922 9.89343 12.133 9.89343C17.3708 9.89343 22.3266 12.5162 24.77 16.5789C26.0575 18.5823 26.5838 20.4279 26.5838 22.9392C26.5838 28.4727 21.2928 33.7399 15.4849 33.9907C15.3346 33.9969 15.1873 34 15.0401 34H15.0495Z"
fill="currentColor"
/>
<path
d="M6.81973 26.2184C2.93214 26.2184 0 23.3727 0 19.6011C0 18.1736 0.523149 16.7244 1.55692 15.2938L1.59451 15.2442C1.71668 15.0708 1.85138 14.9005 1.99548 14.7302C2.09573 14.6095 2.42152 14.2224 2.94467 13.7207C3.6965 12.9993 4.8963 13.0178 5.6262 13.761C6.3561 14.5042 6.33731 15.6901 5.58548 16.4116C5.17197 16.808 4.94015 17.0929 4.93702 17.096L4.89316 17.1486C4.82111 17.2291 4.75846 17.3096 4.70521 17.3871L4.64255 17.4769C4.43267 17.7679 3.79048 18.6535 3.79048 19.6042C3.79048 21.2918 5.03413 22.4716 6.81973 22.4716C8.80582 22.4716 12.6339 21.9328 15.9451 18.3191L15.967 18.2974L16.5183 17.7153C17.2326 16.9597 18.4324 16.9195 19.1999 17.6286C19.9642 18.3346 20.005 19.5206 19.2876 20.2792L18.7488 20.849C14.4602 25.5217 9.43548 26.2215 6.82286 26.2215L6.81973 26.2184Z"
fill="currentColor"
/>
<path
d="M9.12705 20.0439C8.19979 20.0439 7.38844 19.372 7.25373 18.4368C6.76818 15.1018 6.60841 12.0703 6.76504 9.16576C6.79324 4.10601 10.9659 0 16.0877 0C21.2096 0 25.4136 4.13388 25.4136 9.2184C25.4136 12.1849 24.5678 13.9375 23.2019 15.8047C22.5879 16.6439 21.4007 16.8297 20.5549 16.2228C19.7059 15.6158 19.518 14.4454 20.132 13.6062C21.1438 12.2251 21.6231 11.2157 21.6231 9.2184C21.6231 6.20237 19.1421 3.74991 16.0909 3.74991C13.0397 3.74991 10.5587 6.20237 10.5587 9.2184V9.32058C10.4083 11.9898 10.5555 14.7953 11.0066 17.9011C11.157 18.9261 10.4365 19.8767 9.39958 20.0253C9.30874 20.0377 9.21789 20.0439 9.12705 20.0439V20.0439Z"
fill="currentColor"
/>
<path
d="M16.4715 17.7618L17.64 16.486C17.8154 16.3126 18.2602 16.2971 18.4701 16.486C18.7458 16.7337 19.2658 17.2942 19.6511 18.2913C19.8516 18.8146 15.964 19.5144 16.4715 17.7649V17.7618Z"
fill="currentColor"
/>
<path
d="M2.87011 13.7703C3.50603 13.1789 4.2234 12.7578 4.87499 12.541C5.36994 12.3769 5.9996 12.3614 6.04346 13.0086C6.09045 13.7208 6.1625 14.5321 6.15937 15.0089C6.15937 15.5663 1.74863 15.2133 2.87011 13.7703Z"
fill="currentColor"
/>
</g>
<path
d="M41.0478 27.359H47.4084C52.0393 27.359 54.2711 25.0993 54.2711 21.8353C54.2711 19.0177 52.4857 17.3717 50.4492 16.8417C51.8441 16.2279 53.0158 14.8052 53.0158 12.7407C53.0158 10.1184 51.2024 7.8308 47.6316 7.8308H41.0478C40.7967 7.8308 40.6293 7.99819 40.6293 8.24926V26.9405C40.6293 27.1916 40.7967 27.359 41.0478 27.359ZM44.5628 23.8439V18.6829H47.1294C49.0543 18.6829 50.226 19.464 50.226 21.2495C50.226 23.0349 49.0543 23.8439 47.1294 23.8439H44.5628ZM44.5628 15.5863V11.3459H46.8504C48.329 11.3459 49.2496 11.8759 49.2496 13.3824C49.2496 14.9168 48.329 15.5863 46.8504 15.5863H44.5628ZM62.6626 27.6938C64.6991 27.6938 66.1777 26.829 67.1262 25.4341L67.182 26.9405C67.182 27.1916 67.3494 27.359 67.6005 27.359H70.3065C70.5576 27.359 70.7529 27.1916 70.7529 26.9405V14.1077C70.7529 13.8566 70.5855 13.6893 70.3344 13.6893H67.6005C67.3494 13.6893 67.182 13.8566 67.182 14.1077L67.1262 15.6142C66.2056 14.1914 64.727 13.3545 62.6626 13.3545C58.8128 13.3545 56.1625 16.479 56.1625 20.5241C56.1625 24.5972 58.8128 27.6938 62.6626 27.6938ZM59.9287 20.5241C59.9287 18.3481 61.4351 16.8696 63.4716 16.8696C65.5081 16.8696 66.9309 18.3202 66.9309 20.5241C66.9309 22.728 65.5081 24.1787 63.4716 24.1787C61.4351 24.1787 59.9287 22.728 59.9287 20.5241ZM74.4964 27.359H77.5372C77.7883 27.359 77.9556 27.1916 77.9556 26.9405V7.8308C77.9556 7.57972 77.7883 7.41234 77.5372 7.41234H74.4964C74.2453 7.41234 74.0779 7.57972 74.0779 7.8308V26.9405C74.0779 27.1916 74.2453 27.359 74.4964 27.359ZM81.6887 27.359H84.7295C84.9806 27.359 85.1479 27.1916 85.1479 26.9405V7.8308C85.1479 7.57972 84.9806 7.41234 84.7295 7.41234H81.6887C81.4376 7.41234 81.2702 7.57972 81.2702 7.8308V26.9405C81.2702 27.1916 81.4376 27.359 81.6887 27.359ZM95.2695 27.6938C97.1944 27.6938 98.9519 27.1358 100.124 25.9641C100.375 25.741 100.403 25.5457 100.263 25.3504L99.2309 23.9276C99.0914 23.7323 98.9519 23.7044 98.7567 23.816C97.6408 24.4577 96.6365 24.653 95.5764 24.653C93.3725 24.653 91.9497 23.7044 91.5312 21.9748H99.8447C101.044 21.9748 101.351 21.2216 101.351 19.8825C101.351 16.4232 98.9519 13.3545 94.7952 13.3545C90.5827 13.3545 87.7651 16.4511 87.7651 20.4683C87.7651 24.5972 90.778 27.6938 95.2695 27.6938ZM91.4754 19.2688C91.8102 17.4554 93.1214 16.5348 94.8231 16.5348C96.4691 16.5348 97.6966 17.4554 97.9197 19.2688H91.4754ZM104.355 27.359H107.368C107.619 27.359 107.787 27.1916 107.787 26.9405V20.1057C107.787 18.1529 108.791 16.8975 110.688 16.8975C111.079 16.8975 111.385 16.9533 111.692 17.037C112.055 17.1206 112.25 17.037 112.25 16.7022V14.2472C112.25 13.9961 112.195 13.8288 111.999 13.6893C111.776 13.5219 111.385 13.3545 110.744 13.3545C109.126 13.3545 108.261 14.4425 107.787 15.8932L107.675 14.1356C107.647 13.8009 107.508 13.6893 107.257 13.6893H104.355C104.104 13.6893 103.937 13.8566 103.937 14.1077V26.9405C103.937 27.1916 104.104 27.359 104.355 27.359ZM116.706 11.2622C117.934 11.2622 118.826 10.3137 118.826 9.11408C118.826 7.94239 117.934 6.99388 116.706 6.99388C115.479 6.99388 114.558 7.94239 114.558 9.11408C114.558 10.3137 115.479 11.2622 116.706 11.2622ZM114.753 26.9405C114.753 27.1916 114.921 27.359 115.172 27.359H118.213C118.464 27.359 118.631 27.1916 118.631 26.9405V14.1077C118.631 13.8566 118.464 13.6893 118.213 13.6893H115.172C114.921 13.6893 114.753 13.8566 114.753 14.1077V26.9405ZM122.391 27.359H125.404C125.655 27.359 125.822 27.1916 125.822 26.9405V19.8546C125.822 17.9297 126.882 16.8975 128.5 16.8975C130.146 16.8975 131.039 17.9297 131.039 19.8546V26.9405C131.039 27.1916 131.206 27.359 131.457 27.359H134.442C134.693 27.359 134.889 27.1916 134.889 26.9405V18.9898C134.889 15.4189 132.796 13.3545 129.951 13.3545C127.914 13.3545 126.603 14.3588 125.822 15.7537L125.71 14.1356C125.71 13.8009 125.543 13.6893 125.292 13.6893H122.391C122.14 13.6893 121.972 13.8566 121.972 14.1077V26.9405C121.972 27.1916 122.14 27.359 122.391 27.359ZM144.989 27.6938C146.914 27.6938 148.672 27.1358 149.843 25.9641C150.094 25.741 150.122 25.5457 149.983 25.3504L148.95 23.9276C148.811 23.7323 148.672 23.7044 148.476 23.816C147.36 24.4577 146.356 24.653 145.296 24.653C143.092 24.653 141.669 23.7044 141.251 21.9748H149.564C150.764 21.9748 151.071 21.2216 151.071 19.8825C151.071 16.4232 148.672 13.3545 144.515 13.3545C140.302 13.3545 137.485 16.4511 137.485 20.4683C137.485 24.5972 140.498 27.6938 144.989 27.6938ZM141.195 19.2688C141.53 17.4554 142.841 16.5348 144.543 16.5348C146.189 16.5348 147.416 17.4554 147.639 19.2688H141.195Z"
fill="currentColor"
/>
<defs>
<clipPath id="clip0_2190_10239">
<rect width="26.5897" height="34" fill="white" />
</clipPath>
</defs>
</svg>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './BallerineLogo';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Header } from '@app/components/molecules/Header';
import { Header } from '@app/components/organisms/Header';
import { QueryParamProvider } from 'use-query-params';
import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6';

Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { useLogoutMutation } from '@app/common/hooks/useLogoutMutation';
import { Input } from '@app/components/atoms/Input';
import { headerNavigationLinks } from '@app/components/molecules/Header/header-navigation-links';
import { Navigation } from '@app/components/molecules/Navigation';
import { UserNavigation } from '@app/components/molecules/UserNavigation';

export const Header = () => {
const { logout } = useLogoutMutation();
return (
<div className="border-b">
<div className="flex h-16 flex-nowrap items-center justify-between px-4">
<div className="flex flex-1 gap-4">
<UserNavigation />
<UserNavigation onLogout={logout} />
<Navigation items={headerNavigationLinks} />
</div>
<div className="flex gap-4">
Original file line number Diff line number Diff line change
@@ -13,7 +13,11 @@ import {
import { Button } from '@app/components/atoms/Button';
import { Avatar, AvatarFallback, AvatarImage } from '@app/components/atoms/Avatar';

export function UserNavigation() {
interface Props {
onLogout: () => void;
}

export function UserNavigation({ onLogout }: Props) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -54,7 +58,7 @@ export function UserNavigation() {
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<DropdownMenuItem onSelect={onLogout}>
<LogOut className="mr-2 h-4 w-4" />
<span>Log out</span>
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { useFormField } from './hooks/useFormField/useFormField';

export const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();

return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
aria-invalid={!!error}
{...props}
/>
);
});
FormControl.displayName = 'FormControl';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ctw } from '@app/common/utils/ctw/ctw';
import * as React from 'react';
import { useFormField } from './hooks/useFormField/useFormField';

export const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();

return (
<p
ref={ref}
id={formDescriptionId}
className={ctw('text-muted-foreground text-sm', className)}
{...props}
/>
);
});
FormDescription.displayName = 'FormDescription';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Controller, ControllerProps, FieldPath, FieldValues } from 'react-hook-form';
import { FormFieldContext } from './Form.FieldContext';

export const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as React from 'react';
import { FormFieldContextValue } from './types';

export const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ctw } from '@app/common/utils/ctw/ctw';
import * as React from 'react';
import { FormItemContext } from './Form.ItemContext';

export const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const id = React.useId();

return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={ctw('space-y-2', className)} {...props} />
</FormItemContext.Provider>
);
},
);
FormItem.displayName = 'FormItem';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as React from 'react';
import { FormItemContextValue } from './types';

export const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from 'react';
import * as LabelPrimitive from '@radix-ui/react-label';
import { useFormField } from './hooks/useFormField/useFormField';
import { Label } from '@app/components/atoms/Label/Label';
import { ctw } from '@app/common/utils/ctw/ctw';

export const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();

return (
<Label
ref={ref}
className={ctw(error && 'text-destructive', className)}
htmlFor={formItemId}
{...props}
/>
);
});
FormLabel.displayName = 'FormLabel';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ctw } from '@app/common/utils/ctw/ctw';
import * as React from 'react';
import { useFormField } from './hooks/useFormField/useFormField';

export const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;

if (!body) {
return null;
}

return (
<p
ref={ref}
id={formMessageId}
className={ctw('text-destructive text-sm font-medium', className)}
{...props}
>
{body}
</p>
);
});
FormMessage.displayName = 'FormMessage';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { FormProvider } from 'react-hook-form';

export const Form = FormProvider;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from 'react';
import { useFormContext } from 'react-hook-form';
import { FormFieldContext } from '../../Form.FieldContext';
import { FormItemContext } from '../../Form.ItemContext';

export const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();

const fieldState = getFieldState(fieldContext.name, formState);

if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>');
}

const { id } = itemContext;

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};
12 changes: 12 additions & 0 deletions apps/workflows-dashboard/src/components/organisms/Form/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { FieldPath, FieldValues } from 'react-hook-form';

export type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};

export type FormItemContextValue = {
id: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useLogoutMutation } from '@app/common/hooks/useLogoutMutation';
import { Input } from '@app/components/atoms/Input';
import { Navigation } from '@app/components/molecules/Navigation';
import { UserNavigation } from '@app/components/molecules/UserNavigation';
import { headerNavigationLinks } from './header-navigation-links';

export const Header = () => {
const { logout } = useLogoutMutation();

return (
<div className="border-b">
<div className="flex h-16 flex-nowrap items-center justify-between px-4">
<div className="flex flex-1 gap-4">
<UserNavigation onLogout={logout} />
<Navigation items={headerNavigationLinks} />
</div>
<div className="flex gap-4">
<div>
<Input placeholder="Search" />
</div>
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { NavigationLink } from '@app/components/molecules/Navigation';

export const headerNavigationLinks: NavigationLink[] = [
{
path: '/overview',
label: 'Overview',
},
{
path: '/workflows',
label: 'Workflows',
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Header';
2 changes: 2 additions & 0 deletions apps/workflows-dashboard/src/domains/auth/api/login/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './login.api';
export * from './login.types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { GetSignInDto, GetSignInResponse } from '@app/domains/auth/api/login/login.types';
import { request } from '@app/lib/request';

export async function fetchSignIn(dto: GetSignInDto): Promise<GetSignInResponse> {
const result = await request.post<GetSignInResponse>('internal/auth/login', dto);

return result.data;
}
10 changes: 10 additions & 0 deletions apps/workflows-dashboard/src/domains/auth/api/login/login.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IUser } from '@app/domains/auth/common/types';

export interface GetSignInDto {
email: string;
password: string;
}

export interface GetSignInResponse {
user: IUser;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './logout.api';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { request } from '@app/lib/request';

export async function fetchLogout(): Promise<boolean> {
await request.post('/internal/auth/logout');

return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './session.api';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { fetchSession } from '@app/domains/auth/api/session/session.api';
import { createQueryKeys } from '@lukemorales/query-key-factory';

export const sessionKeys = createQueryKeys('session', {
details: () => ({
queryKey: ['session'],
queryFn: fetchSession,
}),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { GetSessionResponse } from '@app/domains/auth/api/session/session.types';
import { IUser } from '@app/domains/auth/common/types';
import { request } from '@app/lib/request';

export async function fetchSession(): Promise<IUser | null> {
const result = await request.get<GetSessionResponse>('internal/auth/session');

return result.data.user ? result.data.user : null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IUser } from '@app/domains/auth/common/types';

export interface GetSessionResponse {
user?: IUser;
}
7 changes: 7 additions & 0 deletions apps/workflows-dashboard/src/domains/auth/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface IUser {
id: string;
email: string;
firstName: string;
lastName: string;
roles: string[];
}
5 changes: 4 additions & 1 deletion apps/workflows-dashboard/src/index.css
Original file line number Diff line number Diff line change
@@ -73,7 +73,10 @@
* {
@apply border-border;
}
body {
html,
body,
#root {
height: 100%;
@apply bg-background text-foreground font-inter;
}
}
7 changes: 5 additions & 2 deletions apps/workflows-dashboard/src/lib/request/request.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import axios from 'axios';

export const request = axios.create({ baseURL: import.meta.env.VITE_API_URL });
export const request = axios.create({
baseURL: import.meta.env.VITE_API_URL,
withCredentials: true,
});

request.interceptors.request.use(config => {
if (config.headers) {
config.headers['Authorization'] = `Bearer ${import.meta.env.VITE_API_KEY}`;
config.headers['Authorization'] = `Api-Key ${import.meta.env.VITE_API_KEY}`;
return config;
}
return config;
54 changes: 54 additions & 0 deletions apps/workflows-dashboard/src/pages/SignIn/SignIn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { BallerineLogo } from '@app/components/atoms/icons/BallerineLogo';
import { env } from '@app/common/env/env';
import { SignInForm } from '@app/pages/SignIn/components/SignInForm';
import { useSignInMutation } from '@app/pages/SignIn/hooks/useSignInMutation';
import { Navigate, useNavigate } from 'react-router-dom';
import { getRefererUrl } from '@app/common/hocs/withSessionProtected/utils/get-referer-url';
import { clearRefererUrl } from '@app/common/hocs/withSessionProtected/utils/clear-referer-url';
import { useSession } from '@app/common/hooks/useSession';
import { LoadingSpinner } from '@app/components/atoms/LoadingSpinner';

export function SignIn() {
const navigate = useNavigate();
const { isLoading: isLoadingSession, isAuthenticated, refresh } = useSession();
const { isLoading, errorCode, signIn } = useSignInMutation({
onSuccess: () => {
refresh();
const refUrl = getRefererUrl();
navigate(refUrl || '/');

if (refUrl) {
clearRefererUrl();
}
},
});

if (isLoadingSession) {
return (
<div className={`flex h-full flex-col items-center justify-center`}>
<LoadingSpinner />
</div>
);
}

if (!isLoadingSession && isAuthenticated) {
return <Navigate to="/" replace />;
}

return (
<section className={`flex h-full flex-col items-center justify-center`}>
<div className={`mb-16`}>
{env.VITE_IMAGE_LOGO_URL ? (
<img className={`w-40`} src={env.VITE_IMAGE_LOGO_URL} />
) : (
<BallerineLogo />
)}
</div>
<SignInForm
isSubmitting={isLoading}
alert={errorCode ? <SignInForm.ErrorAlert errorCode={errorCode} /> : null}
onSubmit={values => signIn(values)}
/>
</section>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ErrorAlert } from '@app/components/atoms/ErrorAlert/ErrorAlert';

interface Props {
errorCode: number;
}

const getErrorMessageByErrorCode = (code: number) => {
const defaultMessage = `Something went wrong. Try again later.`;

const codeToMessageMap: Record<number, string> = {
[401]: 'Invalid credentials',
};

return codeToMessageMap[code] || defaultMessage;
};

export function FormErrorAlert({ errorCode }: Props) {
return <ErrorAlert>{getErrorMessageByErrorCode(errorCode)}</ErrorAlert>;
}

FormErrorAlert.displayName = 'FormErrorAlert';
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Button } from '@app/components/atoms/Button';
import { Card, CardContent, CardHeader } from '@app/components/atoms/Card';
import { Input } from '@app/components/atoms/Input';
import { Form } from '@app/components/organisms/Form/Form';
import { FormControl } from '@app/components/organisms/Form/Form.Control';
import { FormField } from '@app/components/organisms/Form/Form.Field';
import { FormItem } from '@app/components/organisms/Form/Form.Item';
import { FormLabel } from '@app/components/organisms/Form/Form.Label';
import { FormMessage } from '@app/components/organisms/Form/Form.Message';
import { SignInFormValues } from '@app/pages/SignIn/components/SignInForm/types';
import { useForm } from 'react-hook-form';
import * as classnames from 'classnames';
import { FormErrorAlert } from '@app/pages/SignIn/components/SignInForm/ErrorAlert';

interface Props {
isSubmitting: boolean;
alert?: React.ReactNode;
onSubmit: (values: SignInFormValues) => void;
}

export function SignInForm({ isSubmitting, alert = null, onSubmit }: Props) {
const form = useForm<SignInFormValues>();

return (
<Card className={`w-full max-w-lg`}>
<CardHeader className={`mb-2 text-center text-4xl font-bold`}>Sign In</CardHeader>
<CardContent>
{alert}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input required type={'email'} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input required type={'password'} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className={`flex justify-end`}>
<Button
type="submit"
className={classnames(`ms-auto mt-3`, {
['opacity-50']: isSubmitting,
['pointer-events-none']: isSubmitting,
})}
>
Sign In
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
);
}

SignInForm.ErrorAlert = FormErrorAlert;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SignInForm';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface SignInFormValues {
email: string;
password: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useSignInMutation';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { fetchSignIn, GetSignInDto, GetSignInResponse } from '@app/domains/auth/api/login';
import { useMutation } from '@tanstack/react-query';
import { AxiosError } from 'axios';

export interface UseSignInParams {
onSuccess?: () => void;
}

export function useSignInMutation({ onSuccess }: UseSignInParams) {
const mutation = useMutation<GetSignInResponse, AxiosError, GetSignInDto>({
mutationFn: fetchSignIn,
onSuccess,
});

return {
isLoading: mutation.isLoading,
errorCode: mutation.error?.response ? mutation.error.response.status : null,
signIn: mutation.mutate,
};
}
1 change: 1 addition & 0 deletions apps/workflows-dashboard/src/pages/SignIn/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SignIn';
4 changes: 2 additions & 2 deletions apps/workflows-dashboard/src/pages/Workflows/Workflows.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Pagination } from '@app/components/molecules/Pagination';
import { StatusFilterComponent } from '@app/pages/Workflows/components/molecules/StatusFilterComponent';
import { FilterComponent } from '@app/pages/Workflows/components/organisms/WorkflowFilters/types';
import { useWorkflows } from '@app/pages/Workflows/hooks/useWorkflows';
import { useWorkflowsQuery } from '@app/pages/Workflows/hooks/useWorkflowsQuery';
import { useWorkflowsFilters } from '@app/pages/Workflows/hooks/useWorkflowsFilters';
import { useCallback } from 'react';
import { WorkflowsList } from '@app/pages/Workflows/components/organisms/WorkflowsList';
@@ -16,7 +16,7 @@ const filterComponents: FilterComponent[] = [StatusFilterComponent];
export const Workflows = () => {
const { filters, setFilters } = useWorkflowsFilters();
const { sortingKey, sortingDirection } = useSorting('order_by');
const { data, isLoading, isFetching } = useWorkflows(
const { data, isLoading, isFetching } = useWorkflowsQuery(
filters,
sortingKey && sortingDirection
? { orderBy: sortingKey, orderDirection: sortingDirection }

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useWorkflowsQuery';
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { workflowKeys } from '@app/domains/workflows';
import { WorkflowsFilterValues } from '@app/pages/Workflows/hooks/useWorkflowsFilters/types';
import { useQuery } from '@tanstack/react-query';

export function useWorkflows(query: WorkflowsFilterValues, sortingParams?: SortingParams) {
export function useWorkflowsQuery(query: WorkflowsFilterValues, sortingParams?: SortingParams) {
const {
isFetching,
isLoading,
10 changes: 8 additions & 2 deletions apps/workflows-dashboard/src/router.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { App } from '@app/App';
import { withSessionProtected } from '@app/common/hocs/withSessionProtected';
import { Overview } from '@app/pages/Overview';
import { SignIn } from '@app/pages/SignIn';
import { Workflows } from '@app/pages/Workflows';
import { createBrowserRouter, Navigate } from 'react-router-dom';

@@ -12,13 +14,17 @@ export const router = createBrowserRouter([
path: '',
element: <Navigate to="/overview" replace />,
},
{
path: '/auth/signin',
Component: SignIn,
},
{
path: 'overview',
Component: Overview,
Component: withSessionProtected(Overview),
},
{
path: 'workflows',
Component: Workflows,
Component: withSessionProtected(Workflows),
},
],
},
3 changes: 2 additions & 1 deletion apps/workflows-dashboard/tsconfig.json
Original file line number Diff line number Diff line change
@@ -21,7 +21,8 @@
"noFallthroughCasesInSwitch": true,
"paths": {
"@app/*": ["./src/*"]
}
},
"types": ["node", "jest", "vite-plugin-terminal/client"]
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
14 changes: 13 additions & 1 deletion apps/workflows-dashboard/vite.config.ts
Original file line number Diff line number Diff line change
@@ -3,10 +3,22 @@ import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import tailwindcss from 'tailwindcss';
import checker from 'vite-plugin-checker';
import terminal from 'vite-plugin-terminal';

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss(), checker({ typescript: true })],
server: {
port: 5200,
},
plugins: [
react(),
tailwindcss(),
checker({ typescript: true }),
terminal({
output: ['console', 'terminal'],
strip: false,
}),
],
resolve: {
alias: {
'@app': resolve(__dirname, './src'),
28 changes: 26 additions & 2 deletions pnpm-lock.yaml
1 change: 1 addition & 0 deletions services/workflows-service/.env.example
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ DB_PASSWORD=admin
DB_PORT=5432
DB_URL=postgres://admin:admin@localhost:5432/postgres
SESSION_SECRET=iGdnj4A0YOhj8dHJK7IWSvQKEZsG7P70FFehuddhFPjtg/bSkzFejYILk4Xue6Ilx9y3IAwzR8pV1gb4
WORKFLOW_DASHBOARD_CORS_ORIGIN=http://localhost:5174
BACKOFFICE_CORS_ORIGIN=http://localhost:5137
HEADLESS_EXAMPLE_CORS_ORIGIN=http://localhost:5173
API_KEY=secret
1 change: 1 addition & 0 deletions services/workflows-service/src/env.ts
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ export const env = createEnv({
SESSION_SECRET: z.string(),
BACKOFFICE_CORS_ORIGIN: z.string().url(),
HEADLESS_EXAMPLE_CORS_ORIGIN: z.string().url(),
WORKFLOW_DASHBOARD_CORS_ORIGIN: z.string().url(),
AWS_S3_BUCKET_NAME: z.string().optional(),
AWS_S3_BUCKET_KEY: z.string().optional(),
AWS_S3_BUCKET_SECRET: z.string().optional(),
3 changes: 2 additions & 1 deletion services/workflows-service/src/main.ts
Original file line number Diff line number Diff line change
@@ -19,10 +19,11 @@ global.__rootdir__ = __dirname || process.cwd();

const corsOrigins =
env.NODE_ENV === 'production'
? [env.BACKOFFICE_CORS_ORIGIN, /\.ballerine\.app$/]
? [env.BACKOFFICE_CORS_ORIGIN, env.WORKFLOW_DASHBOARD_CORS_ORIGIN, /\.ballerine\.app$/]
: [
env.BACKOFFICE_CORS_ORIGIN,
env.HEADLESS_EXAMPLE_CORS_ORIGIN,
env.WORKFLOW_DASHBOARD_CORS_ORIGIN,
/\.ballerine\.dev$/,
/\.ballerine\.app$/,
];