diff --git a/services/credential-server-ui/eslint.config.js b/services/credential-server-ui/eslint.config.js index 092408a9f..e67846f70 100644 --- a/services/credential-server-ui/eslint.config.js +++ b/services/credential-server-ui/eslint.config.js @@ -1,28 +1,29 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ['dist'] }, + { ignores: ["dist"] }, { extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], + "@typescript-eslint/no-unused-vars": "off", }, - }, -) + } +); diff --git a/services/credential-server-ui/src/components/PopupModal/PopupModal.scss b/services/credential-server-ui/src/components/PopupModal/PopupModal.scss new file mode 100644 index 000000000..624b6f1a5 --- /dev/null +++ b/services/credential-server-ui/src/components/PopupModal/PopupModal.scss @@ -0,0 +1,69 @@ +.popup-modal.MuiModal-root { + display: flex; + align-items: center; + justify-content: center; + + .popup-modal-container { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 29.25rem; + min-height: 13.25rem; + padding: 1.5rem; + background-color: var(--color-neutral-100); + border-radius: 1.5rem; + box-shadow: 0.25rem 0.25rem 1.25rem 0rem rgba(var(--text-color-rgb), 0.16); + + .popup-modal-header { + display: flex; + flex-direction: row; + justify-content: space-between; + margin-bottom: 0.5rem; + + h2 { + margin: 0; + } + + button { + width: 1.5rem; + height: 1.5rem; + padding: 0.25rem; + color: var(--text-color); + + &:hover, + &:active { + background-color: var(--color-neutral-300); + } + + svg { + width: 1.25rem; + height: 1.25rem; + } + } + } + + .popup-modal-body { + font-weight: 500; + text-align: left; + margin-bottom: 1.5rem; + } + + .popup-modal-footer { + display: flex; + justify-content: space-between; + + button { + text-transform: none; + padding: 0.75rem 1.25rem; + border-radius: 1rem; + box-shadow: none; + width: 12.75rem; + + &:first-of-type { + color: var(--text-color); + background-color: var(--color-neutral-100); + } + } + } + } +} diff --git a/services/credential-server-ui/src/components/PopupModal/PopupModal.tsx b/services/credential-server-ui/src/components/PopupModal/PopupModal.tsx new file mode 100644 index 000000000..1ef18b44d --- /dev/null +++ b/services/credential-server-ui/src/components/PopupModal/PopupModal.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { Modal, Fade, Box, IconButton, Typography } from "@mui/material"; +import { Close } from "@mui/icons-material"; +import "./PopupModal.scss"; + +interface PopupModalProps { + open: boolean; + onClose: () => void; + title: string; + body: React.ReactNode; + footer: React.ReactNode; +} + +const PopupModal: React.FC = ({ + open, + onClose, + title, + body, + footer, +}) => { + return ( + + + +
+

{title}

+ + + +
+ {body} +
{footer}
+
+
+
+ ); +}; + +export { PopupModal }; diff --git a/services/credential-server-ui/src/components/PopupModal/index.ts b/services/credential-server-ui/src/components/PopupModal/index.ts new file mode 100644 index 000000000..9765a873d --- /dev/null +++ b/services/credential-server-ui/src/components/PopupModal/index.ts @@ -0,0 +1 @@ +export * from "./PopupModal"; diff --git a/services/credential-server-ui/src/locales/en/en.json b/services/credential-server-ui/src/locales/en/en.json index 4c510d02b..186e1a710 100644 --- a/services/credential-server-ui/src/locales/en/en.json +++ b/services/credential-server-ui/src/locales/en/en.json @@ -26,7 +26,13 @@ "issueCredential": "Issue credential", "connectionName": "Connection Name", "connectionDate": "Connection Date", - "issuedCredentials": "Issued Credentials" + "issuedCredentials": "Issued Credentials", + "deleteConnections": { + "title": "Delete Connection(s)", + "body": "Are you sure you want to delete this connection? This action cannot be undone.", + "cancel": "Cancel", + "delete": "Delete" + } }, "credentials": { "title": "Credentials" diff --git a/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/ConnectionsTable.tsx b/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/ConnectionsTable.tsx index 53dc19d98..f29a41729 100644 --- a/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/ConnectionsTable.tsx +++ b/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/ConnectionsTable.tsx @@ -26,7 +26,7 @@ import { fetchContactCredentials, } from "../../../../store/reducers/connectionsSlice"; import { RootState, AppDispatch } from "../../../../store"; -import { Contact, Data } from "../../../../types"; +import { Contact, Data } from "../ConnectionsTable/ConnectionsTable.types"; import { menuItems } from "./menuItems"; import { generateRows } from "./helpers"; @@ -40,9 +40,9 @@ const ConnectionsTable: React.FC = () => { ); const [filteredContacts, setFilteredContacts] = useState([]); // TODO: implement search filter - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [connectionsFilterBySearch, setConnectionsFilterBySearch] = useState(""); + const [numSelected, setNumSelected] = useState(0); useEffect(() => { dispatch(fetchContacts()); @@ -73,6 +73,7 @@ const ConnectionsTable: React.FC = () => { order, orderBy, selected, + setSelected, page, rowsPerPage, handleRequestSort, @@ -82,7 +83,7 @@ const ConnectionsTable: React.FC = () => { handleChangeRowsPerPage, emptyRows, visibleRows, - } = useTable(rows); + } = useTable(rows, setNumSelected); const ActionButton = ( { return ( - + void; + selected: string[]; + setSelected: (selected: string[]) => void; } const EnhancedTableToolbar: React.FC = (props) => { - const { numSelected } = props; - const handleOpenModal = () => - setState((prevState) => ({ ...prevState, openModal: true })); + const { numSelected, setNumSelected, selected, setSelected } = props; + const [openModal, setOpenModal] = useState(false); + const dispatch = useDispatch(); + + const handleDelete = async () => { + console.log("delete connections"); + for (const id of selected) { + await handleDeleteContact(id, dispatch); + } + setNumSelected(0); + setSelected([]); + setOpenModal(false); + }; + return ( <> @@ -41,7 +60,7 @@ const EnhancedTableToolbar: React.FC = (props) => { aria-label="delete connections" startIcon={} className="delete-connections-button" - onClick={handleOpenModal} + onClick={() => setOpenModal(true)} > {i18n.t("pages.connections.delete")} @@ -49,12 +68,30 @@ const EnhancedTableToolbar: React.FC = (props) => { )} - {/* - setState((prevState) => ({ ...prevState, openModal: open })) + setOpenModal(false)} + title={i18n.t("pages.connections.deleteConnections.title")} + body={i18n.t("pages.connections.deleteConnections.body")} + footer={ + <> + + + } - /> */} + /> ); }; diff --git a/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/helpers.ts b/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/helpers.ts index 2479f2ce8..c2b7f510f 100644 --- a/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/helpers.ts +++ b/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/helpers.ts @@ -1,4 +1,4 @@ -import { Contact, Credential, Data } from "../../../../types"; +import { Contact, Credential, Data } from "./ConnectionsTable.types"; import axios from "axios"; import { config } from "../../../../config"; import { fetchContacts } from "../../../../store/reducers/connectionsSlice"; diff --git a/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/useTable.ts b/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/useTable.ts index f8dc4f264..7c043e8ce 100644 --- a/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/useTable.ts +++ b/services/credential-server-ui/src/pages/Connections/components/ConnectionsTable/useTable.ts @@ -1,5 +1,5 @@ -import { useState } from "react"; -import { Data } from "../../components/ConnectionsTable/ConnectionsTable.types"; +import { useState, useEffect } from "react"; +import { Data } from "../ConnectionsTable/ConnectionsTable.types"; type Order = "asc" | "desc"; @@ -16,10 +16,7 @@ const descendingComparator = (a: T, b: T, orderBy: keyof T) => { const getComparator = ( order: Order, orderBy: Key -): (( - a: { [key in Key]: number | string }, - b: { [key in Key]: number | string } -) => number) => { +): ((a: Data, b: Data) => number) => { return order === "desc" ? (a, b) => descendingComparator(a, b, orderBy) : (a, b) => -descendingComparator(a, b, orderBy); @@ -35,13 +32,20 @@ const stableSort = (array: T[], comparator: (a: T, b: T) => number) => { return stabilizedThis.map((el) => el[0]); }; -export const useTable = (rows: Data[]) => { +export const useTable = ( + rows: Data[], + setNumSelected: (num: number) => void +) => { const [order, setOrder] = useState("asc"); const [orderBy, setOrderBy] = useState("name"); const [selected, setSelected] = useState([]); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(5); + useEffect(() => { + setNumSelected(selected.length); + }, [selected, setNumSelected]); + const handleRequestSort = ( event: React.MouseEvent, property: keyof Data @@ -103,6 +107,7 @@ export const useTable = (rows: Data[]) => { order, orderBy, selected, + setSelected, page, rowsPerPage, handleRequestSort, diff --git a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.scss b/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.scss deleted file mode 100644 index 39167c263..000000000 --- a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.scss +++ /dev/null @@ -1,19 +0,0 @@ -.delete-connection-modal-container { - position: "absolute"; - top: 0; - left: 0; - width: 29.25rem; - height: 13.25rem; - padding: 1.25rem; - - button { - border-radius: 1rem; - text-transform: none; - color: var(--text-color); - - &:hover, - &:active { - background-color: var(--color-primary-200); - } - } -} diff --git a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.tsx b/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.tsx deleted file mode 100644 index f2478c95f..000000000 --- a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { DeleteConnectionModalProps } from "./DeleteConnectionModal.types"; -import "./HolderModal.scss"; -import Grid from "@mui/material/Grid2"; -import { ChevronLeft } from "@mui/icons-material"; -import { Modal, Fade, Box, IconButton, Typography } from "@mui/material"; - -const DeleteConnectionModal = ({ - openModal, - setOpenModal, -}: DeleteConnectionModalProps) => { - const handleCloseModal = () => setOpenModal(false); - - return ( - - - - - - - - - - - - Text in a modal - - - - - - - - ); -}; - -export { DeleteConnectionModal }; diff --git a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.types.ts b/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.types.ts deleted file mode 100644 index d90509c62..000000000 --- a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/DeleteConnectionModal.types.ts +++ /dev/null @@ -1,6 +0,0 @@ -interface DeleteConnectionModalProps { - openModal: boolean; - setOpenModal: (openModal: boolean) => void; -} - -export type { DeleteConnectionModalProps }; diff --git a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/index.ts b/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/index.ts deleted file mode 100644 index 9d63d5fe8..000000000 --- a/services/credential-server-ui/src/pages/Connections/components/DeleteConnectionModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./DeleteConnectionModal"; diff --git a/services/credential-server-ui/src/theme/theme.ts b/services/credential-server-ui/src/theme/theme.ts index cea7e2106..4ca4ac913 100644 --- a/services/credential-server-ui/src/theme/theme.ts +++ b/services/credential-server-ui/src/theme/theme.ts @@ -10,6 +10,9 @@ const theme = createTheme({ fontSize: 16, fontWeight: 600, }, + h2: { + fontSize: 28, + }, h6: { fontSize: 24, },