Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1,111 changes: 90 additions & 1,021 deletions frontend/package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
"prettier": "../.github/configurations/node_linters/prettier/.prettierrc.js"
},
"dependencies": {
"@greedybear/gb-ui": "github:GreedyBear-Project/gb-ui#1.0.1",
"@tanstack/react-table": "^8.21.3",
"@vnedyalk0v/react19-simple-maps": "^2.0.10",
"axios": "^1.20.0",
"axios-hooks": "^5.1.1",
"bootstrap": ">=5.3.8",
"classnames": "^2.5.1",
"date-fns": "^2.30.0",
"formik": "^2.4.9",
"i18n-iso-countries": "^7.14.0",
"nanoid": "^3.3.11",
"prop-types": "^15.8.1",
"react": "^19.2.8",
"react-dom": "^19.2.8",
Expand All @@ -34,6 +37,9 @@
"preview": "vite preview",
"test": "vitest",
"test-coverage": "vitest run --coverage",
"start:visual": "vite --host 127.0.0.1 --port 3002",
"test:visual": "playwright test",
"test:visual:update": "playwright test --update-snapshots",
"lint-config-install": "cd ../.github/configurations/node_linters/eslint && npm i",
"lint": "eslint 'src/**/*.{js,jsx}' 'tests/**/*.{js,jsx}'",
"lint-fix": "npm run lint -- --fix",
Expand All @@ -47,6 +53,7 @@
"d3-color": "^3.1.0"
},
"devDependencies": {
"@playwright/test": "^1.62.1",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
"@testing-library/user-event": "^14.6.6",
Expand Down
33 changes: 33 additions & 0 deletions frontend/playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
testDir: "./tests/visual",
fullyParallel: true,
reporter: "html",
snapshotPathTemplate: "{testDir}/{testFilePath}-snapshots/{arg}{ext}",
use: {
baseURL: "http://127.0.0.1:3002",
trace: "on-first-retry",
},
projects: [
{
name: "desktop-chromium",
use: {
...devices["Desktop Chrome"],
viewport: { width: 1440, height: 1200 },
},
},
{
name: "mobile-chromium",
use: {
...devices["Pixel 7"],
},
},
],
webServer: {
command: "npm run start:visual",
url: "http://127.0.0.1:3002/tests/visual.html",
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});
2 changes: 1 addition & 1 deletion frontend/src/components/Routes.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Suspense } from "react";
import { FallBackLoading } from "@greedybear/gb-ui";
import { FallBackLoading } from "./common/gb-ui/index";

import IfAuthRedirectGuard from "../wrappers/ifAuthRedirectGuard";
import AuthGuard from "../wrappers/AuthGuard";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/auth/EmailVerification.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { Navigate, useSearchParams } from "react-router-dom";
import { Spinner } from "reactstrap";
import { ContentSection } from "@greedybear/gb-ui";
import { ContentSection } from "../common/gb-ui/index";

import { verifyEmail } from "./api";

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/auth/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { MdInfoOutline } from "react-icons/md";
import { Form, Formik } from "formik";

import { ContentSection } from "@greedybear/gb-ui";
import { ContentSection } from "../common/gb-ui/index";

import { useAuthStore } from "../../stores";
import {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/auth/Logout.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { useShallow } from "zustand/shallow";

import { FallBackLoading } from "@greedybear/gb-ui";
import { FallBackLoading } from "../common/gb-ui/index";

import { useAuthStore } from "../../stores";
import { AUTHENTICATION_STATUSES } from "../../constants";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/auth/Register.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { Form, Formik } from "formik";
import { useTitle } from "react-use";

import { ContentSection, Select } from "@greedybear/gb-ui";
import { ContentSection, Select } from "../common/gb-ui/index";

import {
AfterRegistrationModalAlert,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/auth/ResetPassword.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FormGroup, Label, Col, Input, Button, Spinner } from "reactstrap";
import { Form, Formik } from "formik";
import { useTitle } from "react-use";

import { ContentSection } from "@greedybear/gb-ui";
import { ContentSection } from "../common/gb-ui/index";

import { UUID_REGEX } from "../../constants/index";
import { resetPassword } from "./api";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/auth/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from "axios";

import { addToast } from "@greedybear/gb-ui";
import { addToast } from "../common/gb-ui/index";

import { AUTH_BASE_URI } from "../../constants/api";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
// import PropTypes from "prop-types";
import { Alert, PopoverBody } from "reactstrap";
import { IoMail } from "react-icons/io5";
import { PopupFormButton } from "@greedybear/gb-ui";
import { PopupFormButton } from "../../common/gb-ui/index";
import EmailForm from "./EmailForm";
import { resendVerificationMail, requestPasswordReset } from "../api";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";
import classnames from "classnames";
import { Alert, Col } from "reactstrap";
import { MdError } from "react-icons/md";

const sizeToWidth = {
sm: 4,
md: 8,
lg: 10,
xl: 12,
};

function ErrorAlert({ error, size = "md", className = undefined, ...rest }) {
return (
<Alert
tag={Col}
md={sizeToWidth[size]}
color="danger"
className={classnames("mx-auto m-3", className)}
{...rest}
>
<h4>
<MdError />
&nbsp; Error!
</h4>
<hr />
<h5>
{[error?.response?.status, error?.response?.statusText]
.filter(Boolean)
.join(" | ")}
</h5>
<div className="p-2 border border-darker">
<code className="text-light">{error?.parsedMsg?.toString()}</code>
</div>
</Alert>
);
}

export default ErrorAlert;
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from "react";
import { Tooltip, UncontrolledTooltip } from "reactstrap";
import { HiClipboardCopy } from "react-icons/hi";

import useCopyToClipboard from "react-use/lib/useCopyToClipboard";

function CopyToClipboardButton({
id,
text,
children = null,
tooltip = null,
showOnHover = false,
...rest
}) {
// props

// local state
const [copied, setCopied] = React.useState(false);
const timeoutRef = React.useRef(null);

const [, copyToClipboard] = useCopyToClipboard();

React.useEffect(
() => () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
},
[],
);

// callbacks
const onCopy = () => {
copyToClipboard(text);
setCopied(true);
if (timeoutRef.current) clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(
() => setCopied(false),
tooltip?.timeout || 1000,
);
};

return (
<>
<span
id={`copyBtn-${id}`}
role="button"
tabIndex={0}
className="pointer"
onClick={onCopy}
onKeyPress={onCopy}
{...rest}
>
{children || <HiClipboardCopy size="1.5em" />}
</span>
<Tooltip
isOpen={copied}
placement="top"
target={`copyBtn-${id}`}
trigger="click"
innerClassName="bg-info text-darker"
delay={{ show: 0, hide: 0 }}
>
{tooltip?.text || "Copied!"}
</Tooltip>
{showOnHover && (
<UncontrolledTooltip target={`copyBtn-${id}`} trigger="hover">
Click to copy
</UncontrolledTooltip>
)}
</>
);
}

export default CopyToClipboardButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react";
import { Button, UncontrolledTooltip } from "reactstrap";
import { nanoid } from "nanoid";

function IconButton({
id = undefined,
title = undefined,
titlePlacement = "right-start",
Icon,
...rest
}) {
// props

// vars
const btnId = React.useMemo(() => id || `iconbtn-${nanoid(4)}`, [id]);

return (
<Button id={btnId} type="button" {...rest}>
<Icon />
{title && (
<UncontrolledTooltip
target={btnId}
placement={titlePlacement}
delay={{ show: 0 }}
>
{title}
</UncontrolledTooltip>
)}
</Button>
);
}

export default IconButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from "react";
import { Button, Popover, UncontrolledTooltip, PopoverBody } from "reactstrap";
import { nanoid } from "nanoid";
import { IoMdClose } from "react-icons/io";

export default function PopupFormButton({
id = undefined,
title = null,
titlePlacement = "right-start",
popOverPlacement = "right-start",
Icon,
Form,
onFormSuccess = () => null,
...rest
}) {
// props

// state
const [popoverOpen, setPopoverOpen] = React.useState(false);
const closeTimeoutRef = React.useRef(null);

React.useEffect(
() => () => {
if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
},
[],
);

// callbacks
const onFormSubmit = React.useCallback(() => {
if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
closeTimeoutRef.current = setTimeout(() => setPopoverOpen(false), 400);
onFormSuccess();
}, [onFormSuccess]);

// vars
const btnId = React.useMemo(() => id || `popover-btn-${nanoid(4)}`, [id]);

return (
<>
<Button
id={btnId}
type="button"
className="btn-sm"
color="info"
{...rest}
>
{popoverOpen ? <IoMdClose /> : <Icon />}
</Button>
{title && (
<UncontrolledTooltip
target={btnId}
placement={titlePlacement}
delay={{ show: 0 }}
>
{title}
</UncontrolledTooltip>
)}
<Popover
placement={popOverPlacement}
isOpen={popoverOpen}
target={btnId}
toggle={() => setPopoverOpen(!popoverOpen)}
>
<PopoverBody className="bg-dark">
<Form onFormSubmit={onFormSubmit} />
</PopoverBody>
</Popover>
</>
);
}
Loading
Loading