done user and role table, and add user

This commit is contained in:
2026-03-18 17:24:15 +08:00
parent f84894b6d7
commit 14884f6b3a
14 changed files with 298 additions and 134 deletions

View File

@@ -0,0 +1,32 @@
import React from "react";
const plus = () => {
return (
<>
<svg
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 5.29199V18.7087"
stroke="#333649"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.29102 12H18.7077"
stroke="#333649"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</>
);
};
export default plus;

View File

@@ -0,0 +1,36 @@
import React from "react";
import styles from "./styles.module.css";
import MobileSearchBar from "@/app/components/mobileSearchBar/MobileSearchBar";
import useIsMobile from "@/app/hooks/useIsMobile";
import PlusIcon from "../icons/plus";
const Toggle = ({ tabs = [], activeTab, onChange }) => {
const isMobile = useIsMobile();
return (
<div className={styles.toggleContainer}>
<div className={styles.buttonToggles}>
{tabs.map((tab) => (
<button
key={tab.value}
className={`${styles.buttonStyle} ${activeTab === tab.value ? styles.active : ""}`}
onClick={() => onChange(tab.value)}
>
{tab.label}
</button>
))}
</div>
{isMobile && activeTab === "permissions" && (
<div className={styles.container}>
<MobileSearchBar />
<button>
<PlusIcon />
</button>
</div>
)}
</div>
);
};
export default Toggle;

View File

@@ -0,0 +1,56 @@
.toggleContainer {
display: none;
}
@media (max-width: 950px) {
.toggleContainer {
display: flex;
flex-direction: column;
align-self: stretch;
gap: 16px;
}
.buttonToggles {
display: flex;
align-items: flex-start;
gap: 16px;
align-self: stretch;
}
.container {
display: flex;
align-items: center;
gap: 16px;
flex: 1 0 0;
}
.container > button {
display: flex;
padding: 8px;
border: none;
justify-content: center;
align-items: center;
gap: 12px;
border-radius: 6px;
background: #27293b;
}
.buttonStyle {
display: flex;
background: none;
border: none;
padding: 24px 4px 16px 4px;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 5px;
color: #85869b;
font-family: Inter;
font-size: 18px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.18px;
}
.active {
border-bottom: 1px solid #959aff;
color: #959aff;
}
}

View File

@@ -91,7 +91,7 @@
background: #2d3143; background: #2d3143;
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.25); box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.25);
position: absolute; position: absolute;
z-index: 4; z-index: 30;
top: 100%; top: 100%;
margin-top: 8px; margin-top: 8px;
left: 0; left: 0;

View File

@@ -16,10 +16,9 @@
gap: 10px; gap: 10px;
border-radius: 6px; border-radius: 6px;
} }
.profileBadge:hover { .profileBadgeButton:hover {
border-radius: 6px; border-radius: 6px;
background: #21232f; background: #21232f;
background: color(display-p3 0.1294 0.1373 0.1804);
} }
.nameRole { .nameRole {
display: flex; display: flex;
@@ -79,12 +78,10 @@
left: 0; left: 0;
border-radius: 6px; border-radius: 6px;
background: #2d3144; background: #2d3144;
background: color(display-p3 0.1787 0.1913 0.2605);
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.25); box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.25);
box-shadow: 0 2px 15px 0 color(display-p3 0 0 0 / 0.25);
cursor: pointer; cursor: pointer;
position: absolute; position: absolute;
z-index: 10; z-index: 30;
top: 100%; top: 100%;
margin-top: 3px; margin-top: 3px;
animation-name: dropDownAnimation; animation-name: dropDownAnimation;

View File

@@ -3,46 +3,24 @@ import DeleteIcon from "../icons/delete";
import FileIcon from "../icons/file"; import FileIcon from "../icons/file";
import editUserStyle from "./styles.module.css"; import editUserStyle from "./styles.module.css";
import React from "react"; import React from "react";
import PlusIcon from "../icons/plus";
const Permissions = ({ sampleData = [] }) => { const Permissions = ({ sampleData = [] }) => {
return ( return (
<> <>
{/* Permissions */} {/* Permissions */}
<div className={editUserStyle.userPermissions}>
{/* Add permissions */}
<div className={editUserStyle.imanginaryDiv}></div>
<div className={editUserStyle.permissionsContainer}> <div className={editUserStyle.permissionsContainer}>
{/* Header */}
<div className={editUserStyle.permissionsHeader}> <div className={editUserStyle.permissionsHeader}>
<p>Permissions</p> <p>Permissions</p>
<div className={editUserStyle.svgContainer}> <div className={editUserStyle.svgContainer}>
<div> {/* Button ni */}
<svg <button>
xmlns="http://www.w3.org/2000/svg" <PlusIcon />
width={24} </button>
height={24}
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 5.29199V18.7087"
stroke="#333649"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.29102 12H18.7077"
stroke="#333649"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
</div> </div>
</div> </div>
<div className={editUserStyle.wrapper}>
{sampleData.length === 0 ? ( {sampleData.length === 0 ? (
// Default Message // Default Message
<div className={editUserStyle.permissionDefaultState}> <div className={editUserStyle.permissionDefaultState}>

View File

@@ -1,19 +1,3 @@
.imanginaryDiv {
padding: 18px;
}
.userPermissions {
display: flex;
flex-direction: column;
align-items: flex-start;
flex: 1 0 0;
align-self: stretch;
border-radius: 6px;
height: calc(100vh - 175px);
position: relative;
overflow: auto;
scrollbar-width: none;
}
.permissionsContainer { .permissionsContainer {
display: flex; display: flex;
padding: 24px; padding: 24px;
@@ -22,7 +6,6 @@
align-self: stretch; align-self: stretch;
border-radius: 6px; border-radius: 6px;
background: #1d1e2a; background: #1d1e2a;
background: color(display-p3 0.1138 0.1191 0.1616);
} }
.permissionsHeader { .permissionsHeader {
display: flex; display: flex;
@@ -41,7 +24,6 @@
flex: 1 0 0; flex: 1 0 0;
color: #d2d3e1; color: #d2d3e1;
color: color(display-p3 0.8235 0.8275 0.8784);
font-family: Inter; font-family: Inter;
font-size: 20px; font-size: 20px;
font-style: normal; font-style: normal;
@@ -58,21 +40,20 @@
gap: 5px; gap: 5px;
border-radius: 6px; border-radius: 6px;
background: #27293b; background: #27293b;
background: color(display-p3 0.1529 0.1608 0.2275);
} }
.svgContainer > div { .svgContainer > button {
display: flex; display: flex;
padding: 8px; padding: 8px;
border: none;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
border-radius: 6px; border-radius: 6px;
background: #27293b; background: #27293b;
background: color(display-p3 0.1529 0.1608 0.2275);
} }
.permissionDefaultState { .wrapper {
display: flex; display: flex;
height: 43.813px; height: 43.813px;
padding: 8px 16px; padding: 8px 16px;
@@ -80,6 +61,15 @@
align-items: center; align-items: center;
align-self: stretch; align-self: stretch;
} }
.permissionDefaultState {
display: flex;
justify-content: center;
align-items: center;
gap: 12px;
flex: 1 0 0;
align-self: stretch;
}
.permissionDefaultState p { .permissionDefaultState p {
display: flex; display: flex;
justify-content: center; justify-content: center;
@@ -88,7 +78,6 @@
flex: 1 0 0; flex: 1 0 0;
align-self: stretch; align-self: stretch;
color: #85869b; color: #85869b;
color: color(display-p3 0.5216 0.5255 0.6);
font-family: Inter; font-family: Inter;
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
@@ -104,13 +93,10 @@
align-items: center; align-items: center;
align-self: stretch; align-self: stretch;
border-bottom: 0.5px solid #2e3042; border-bottom: 0.5px solid #2e3042;
border-bottom: 0.5px solid color(display-p3 0.1831 0.189 0.2535);
} }
.permissions:hover { .permissions:hover {
border-bottom: 1px solid rgba(129, 135, 255, 0.25); border-bottom: 1px solid rgba(129, 135, 255, 0.25);
border-bottom: 1px solid color(display-p3 0.5098 0.5294 1 / 0.25);
background: rgba(129, 135, 255, 0.05); background: rgba(129, 135, 255, 0.05);
background: color(display-p3 0.5098 0.5294 1 / 0.05);
} }
.permissionsItem { .permissionsItem {
display: flex; display: flex;
@@ -121,7 +107,6 @@
} }
.permissionsItem > p { .permissionsItem > p {
color: #d2d3e1; color: #d2d3e1;
color: color(display-p3 0.8235 0.8275 0.8784);
font-family: Inter; font-family: Inter;
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
@@ -151,35 +136,40 @@
stroke: color(display-p3 1 1 1); stroke: color(display-p3 1 1 1);
border-radius: 100px; border-radius: 100px;
border: 1px solid #959aff; border: 1px solid #959aff;
border: 1px solid color(display-p3 0.5892 0.6031 0.9766);
background: linear-gradient(180deg, #696b95 0%, #20202d 100%); background: linear-gradient(180deg, #696b95 0%, #20202d 100%);
background: linear-gradient(
180deg,
color(display-p3 0.4118 0.4196 0.5725) 0%,
color(display-p3 0.1241 0.1265 0.1725) 100%
);
} }
.permissionDefaultState { @media (max-width: 768px) {
display: flex; .permissionsContainer {
height: 43.813px; border-radius: 4px;
padding: 8px 16px; padding: 0 0 24px 0;
justify-content: center; }
align-items: center; .permissionsHeader {
padding: 12px 16px;
}
.permissionsHeader > p {
color: #fff;
font-size: 18px;
align-self: stretch; align-self: stretch;
} }
.permissionDefaultState p { .svgContainer {
display: none;
}
.wrapper {
display: flex; display: flex;
justify-content: center; padding: 24px 16px 0 16px;
align-items: center; align-items: flex-start;
align-self: stretch;
}
.permissionDefaultState {
gap: 12px; gap: 12px;
flex: 1 0 0;
align-self: stretch; align-self: stretch;
color: #85869b; }
color: color(display-p3 0.5216 0.5255 0.6);
font-family: Inter; .permissionDefaultState > p {
font-size: 16px; font-weight: 500;
font-style: normal; text-align: center;
font-weight: 400; }
line-height: normal;
} }

View File

@@ -9,37 +9,34 @@ import ViewIcon from "../components/icons/view";
import DeleteIcon from "../components/icons/delete"; import DeleteIcon from "../components/icons/delete";
import SuccessToast from "../components/toast/success/successToast"; import SuccessToast from "../components/toast/success/successToast";
import ActionButton from "../components/actionButton/ActionButton"; import ActionButton from "../components/actionButton/ActionButton";
import MobileSearchBar from "../components/mobileSearchBar/MobileSearchBar";
import Card from "./role-card/Card";
const RolesPage = () => { const RolesPage = () => {
const router = useRouter(); const router = useRouter();
const sampleData = [ const sampleData = [
{ {
id: 1,
name: "organization-owner", name: "organization-owner",
orgId: "67160a5ae69144ff19aafb86", orgId: "67160a5ae69144ff19aafb86",
permissions: "35 Permissions", permissions: "35 Permissions",
}, },
{ {
id: 2,
name: "organization-owner", name: "organization-owner",
orgId: "67160a5ae69144ff19aafb86", orgId: "67160a5ae69144ff19aafb86",
permissions: "35 Permissions", permissions: "35 Permissions",
}, },
{ {
id: 3,
name: "organization-owner", name: "organization-owner",
orgId: "67160a5ae69144ff19aafb86", orgId: "67160a5ae69144ff19aafb86",
permissions: "35 Permissions", permissions: "35 Permissions",
}, },
{ {
id: 4,
name: "organization-owner", name: "organization-owner",
orgId: "67160a5ae69144ff19aafb86", orgId: "67160a5ae69144ff19aafb86",
permissions: "35 Permissions", permissions: "35 Permissions",
}, },
{ {
id: 5,
name: "organization-owner", name: "organization-owner",
orgId: "67160a5ae69144ff19aafb86", orgId: "67160a5ae69144ff19aafb86",
permissions: "35 Permissions", permissions: "35 Permissions",
@@ -55,6 +52,21 @@ const RolesPage = () => {
topbarTitle="Roles" topbarTitle="Roles"
requiredButtons={["title", "add", "search"]} requiredButtons={["title", "add", "search"]}
/> />
{/* Mobile View */}
<div className={styles.cardContainer}>
<MobileSearchBar />
{sampleData.map((role, index) => {
return (
<Card
role={role}
key={index}
onClick={() => router.push(`/roles/${index}`)}
/>
);
})}
</div>
{/* Users Table */} {/* Users Table */}
<div className={styles.tableContainer}> <div className={styles.tableContainer}>
<table className={styles.table}> <table className={styles.table}>
@@ -71,8 +83,8 @@ const RolesPage = () => {
//role or user? //role or user?
return ( return (
<tr <tr
key={role.id} key={index}
onClick={() => router.push(`/roles/${role.id}`)} onClick={() => router.push(`/roles/${index}`)}
> >
<td>{role.name}</td> <td>{role.name}</td>
<td>{role.orgId}</td> <td>{role.orgId}</td>

View File

@@ -1,5 +1,6 @@
import DeleteIcon from "@/app/components/icons/delete"; import DeleteIcon from "@/app/components/icons/delete";
import React from "react"; import React from "react";
import styles from "./styles.module.css";
const Card = (props) => { const Card = (props) => {
return ( return (
@@ -7,15 +8,15 @@ const Card = (props) => {
<div className={styles.cardDetails}> <div className={styles.cardDetails}>
<div className={styles.list}> <div className={styles.list}>
<p>Name</p> <p>Name</p>
<p>{props?.user?.email}</p> <p>{props?.role?.name}</p>
</div> </div>
<div className={styles.list}> <div className={styles.list}>
<p>Organization ID</p> <p>Organization ID</p>
<p>{props?.user?.fullName}</p> <p>{props?.role?.orgId}</p>
</div> </div>
<div className={styles.list}> <div className={styles.list}>
<p>Permissions</p> <p>Permissions</p>
<p>{props?.user?.createdAt}</p> <p>{props?.role?.permissions}</p>
</div> </div>
</div> </div>
<div className={styles.cardAction}> <div className={styles.cardAction}>

View File

@@ -23,7 +23,7 @@
text-align: left; text-align: left;
color: #85869b; color: #85869b;
font-family: Inter; font-family: Inter;
font-size: 13px; font-size: var(--table-font-size);
font-style: normal; font-style: normal;
flex: 1 0 0; flex: 1 0 0;
font-weight: 500; font-weight: 500;
@@ -41,7 +41,7 @@
padding: 12px 24px; padding: 12px 24px;
color: #eeeffd; color: #eeeffd;
font-family: Inter; font-family: Inter;
font-size: 13px; font-size: var(--table-font-size);
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
line-height: normal; line-height: normal;
@@ -65,8 +65,12 @@
align-self: stretch; align-self: stretch;
} }
/* Mobile */
.cardContainer { .cardContainer {
display: none;
}
@media (max-width: 768px) {
.cardContainer {
display: flex; display: flex;
padding: 0 16px; padding: 0 16px;
padding-bottom: 50px; padding-bottom: 50px;
@@ -76,4 +80,8 @@
gap: 12px; gap: 12px;
align-self: stretch; align-self: stretch;
overflow: auto; overflow: auto;
}
.tableContainer {
display: none;
}
} }

View File

@@ -1,5 +1,5 @@
"use client"; "use client";
import React from "react"; import React, { useState } from "react";
import TopHeader from "@/app/components/topHeader/TopHeader"; import TopHeader from "@/app/components/topHeader/TopHeader";
import globalStyle from "../../globalStyle.module.css"; import globalStyle from "../../globalStyle.module.css";
import addUserStyle from "./styles.module.css"; import addUserStyle from "./styles.module.css";
@@ -8,6 +8,8 @@ import SelectField from "@/app/components/select/SelectField";
import Permissions from "@/app/components/permissions/Permissions"; import Permissions from "@/app/components/permissions/Permissions";
import useUserForm from "@/app/hooks/useUserForm"; import useUserForm from "@/app/hooks/useUserForm";
import Alert from "@/app/components/alerts/Alert"; import Alert from "@/app/components/alerts/Alert";
import Toggle from "@/app/components/mobileToggleTab/Toggle";
const AddUserPage = () => { const AddUserPage = () => {
const { const {
register, register,
@@ -18,6 +20,9 @@ const AddUserPage = () => {
setTriggerAlert, setTriggerAlert,
} = useUserForm(); } = useUserForm();
// Mobile
const [activeTab, setActiveTab] = useState("details");
return ( return (
<div className={globalStyle.section}> <div className={globalStyle.section}>
{triggerAlert && ( {triggerAlert && (
@@ -36,18 +41,29 @@ const AddUserPage = () => {
state="add" state="add"
requiredButtons={["title", "save"]} requiredButtons={["title", "save"]}
/> />
<div className={addUserStyle.addUserContainer}> <div className={addUserStyle.addUserContainer}>
{/* Mobile Toggle */}
<Toggle
activeTab={activeTab}
onChange={setActiveTab}
tabs={[
{ label: "User Details", value: "details" },
{ label: "Permissions", value: "permissions" },
]}
/>
{/* Input fields Container */} {/* Input fields Container */}
<div className={addUserStyle.inputFieldContainer}> <div className={addUserStyle.inputFieldContainer}>
{/* User Details */} {/* User Details */}
<div className={addUserStyle.userDetails}> <div
{/* Header */} className={`${addUserStyle.userDetails} ${activeTab !== "details" ? addUserStyle.hiddenMobile : ""}`}
>
<div className={addUserStyle.header}> <div className={addUserStyle.header}>
<div> <div>
<p>User Details</p> <p>User Details</p>
</div> </div>
</div> </div>
{/* Input fields */} {/* Ibalhin ning form na tag */}
<form <form
className={addUserStyle.fields} className={addUserStyle.fields}
id="form" id="form"
@@ -68,11 +84,9 @@ const AddUserPage = () => {
{/* Email */} {/* Email */}
<div className={addUserStyle.inputMainContainer}> <div className={addUserStyle.inputMainContainer}>
<div className={addUserStyle.inputContainer}> <div className={addUserStyle.inputContainer}>
{/* Label */}
<div className={addUserStyle.label}> <div className={addUserStyle.label}>
<p>Email</p> <p>Email</p>
</div> </div>
{/* Input field */}
<div className={addUserStyle.inputField}> <div className={addUserStyle.inputField}>
<TextField <TextField
placeHolder="Enter email" placeHolder="Enter email"
@@ -85,11 +99,9 @@ const AddUserPage = () => {
{/* Full Name */} {/* Full Name */}
<div className={addUserStyle.inputMainContainer}> <div className={addUserStyle.inputMainContainer}>
<div className={addUserStyle.inputContainer}> <div className={addUserStyle.inputContainer}>
{/* Label */}
<div className={addUserStyle.label}> <div className={addUserStyle.label}>
<p>Full Name</p> <p>Full Name</p>
</div> </div>
{/* Input field */}
<div className={addUserStyle.inputField}> <div className={addUserStyle.inputField}>
<TextField <TextField
placeHolder="Enter full name" placeHolder="Enter full name"
@@ -103,12 +115,17 @@ const AddUserPage = () => {
</div> </div>
{/* Permissions */} {/* Permissions */}
<div
className={`${addUserStyle.userPermissions} ${activeTab !== "permissions" ? addUserStyle.hiddenMobile : ""}`}
>
<div className={addUserStyle.imanginaryDiv}></div>
<Permissions /> <Permissions />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
); );
}; };

View File

@@ -22,7 +22,22 @@
align-items: flex-start; align-items: flex-start;
border-radius: 6px; border-radius: 6px;
background: #1d1e2a; background: #1d1e2a;
background: color(display-p3 0.1137 0.1176 0.1608); }
.imanginaryDiv {
padding: 18px;
}
.userPermissions {
display: flex;
flex-direction: column;
align-items: flex-start;
flex: 1 0 0;
align-self: stretch;
border-radius: 6px;
height: calc(100vh - 175px);
position: relative;
overflow: auto;
scrollbar-width: none;
} }
.header { .header {
display: flex; display: flex;
@@ -42,7 +57,6 @@
.header > div > p { .header > div > p {
width: 163px; width: 163px;
color: #d2d3e1; color: #d2d3e1;
color: color(display-p3 0.8235 0.8275 0.8784);
font-family: Inter; font-family: Inter;
font-size: 20px; font-size: 20px;
font-style: normal; font-style: normal;
@@ -79,7 +93,6 @@
} }
.label > p { .label > p {
color: #d2d3e1; color: #d2d3e1;
color: color(display-p3 0.8235 0.8275 0.8784);
font-family: Inter; font-family: Inter;
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
@@ -100,6 +113,33 @@
gap: 8px; gap: 8px;
align-self: stretch; align-self: stretch;
} }
.imanginaryDiv {
padding: 18px; @media (max-width: 950px) {
.hiddenMobile {
display: none;
}
.addUserContainer {
align-self: stretch;
padding: 0 16px;
flex-direction: column;
gap: 16px;
}
.header {
display: none;
}
.userDetails {
background: none;
padding: 0px 0px 0px 0px;
margin-top: 16px;
}
.imanginaryDiv {
display: none;
}
}
@media (max-width: 768px) {
.userDetails {
background: none;
padding: 0px 0px 0px 0px;
width: 100%;
}
} }

View File

@@ -22,7 +22,6 @@
text-align: left; text-align: left;
color: #85869b; color: #85869b;
font-family: Inter; font-family: Inter;
font-size: var(--table-font-size); font-size: var(--table-font-size);
font-style: normal; font-style: normal;
flex: 1 0 0; flex: 1 0 0;
@@ -78,7 +77,6 @@
gap: 12px; gap: 12px;
align-self: stretch; align-self: stretch;
overflow: auto; overflow: auto;
background-color: red;
} }
.tableContainer { .tableContainer {
display: none; display: none;

View File

@@ -6,7 +6,6 @@
align-self: stretch; align-self: stretch;
border-bottom: 1px solid #2c2d3d; border-bottom: 1px solid #2c2d3d;
cursor: pointer; cursor: pointer;
background-color: green;
} }
.cardDetails { .cardDetails {
display: flex; display: flex;