added loki form to agents

This commit is contained in:
2026-03-18 08:59:54 +08:00
parent 7745c83bbd
commit 3487a71153
15 changed files with 657 additions and 412 deletions

View File

@@ -6,98 +6,175 @@ import TopToolTip from "@/app/components/topToolTip/TopToolTip";
import globalStyle from "../../globalStyle.module.css"; import globalStyle from "../../globalStyle.module.css";
import createAgentStyle from "./styles.module.css"; import createAgentStyle from "./styles.module.css";
import Prompts from "@/app/components/prompts/Prompts"; import Prompts from "@/app/components/prompts/Prompts";
import useAgentForm from "@/app/hooks/useAgentForm";
import Alert from "@/app/components/alerts/Alert";
import Checkbox from "@/app/components/customCheckbox/Checkbox";
const ViewAgentPage = () => { const ViewAgentPage = () => {
const [isChecked, setIsChecked] = useState(false); const [useVpn, setUseVpn] = useState(true);
const [enableLoki, setEnableLoki] = useState(true);
const handleCheckboxChange = (e) => { const {
setIsChecked(e.target.checked); register,
}; handleSubmit,
errors,
onSubmit,
triggerAlert,
setTriggerAlert,
} = useAgentForm();
return ( return (
<div className={globalStyle.section}> <div className={globalStyle.section}>
{triggerAlert && (
<Alert
setTriggerAlert={setTriggerAlert}
onClick={() => setEditState(true)}
title="Create New Agent"
/>
)}
<div className={globalStyle.mainContainer}> <div className={globalStyle.mainContainer}>
<div className={globalStyle.container}> <div className={globalStyle.container}>
<TopHeader <TopHeader
buttonText="Save" topbarTitle="View Agent"
cancelButtonText="Cancel"
topbarTitle="Update Agent"
state="add" state="add"
requiredButtons={["update", "title"]} requiredButtons={["edit", "back"]}
/> />
{/* Create agent Container */} {/* Create agent Container */}
<form
className={createAgentStyle.formContainer}
id="form"
onSubmit={handleSubmit(onSubmit)}
>
<div className={createAgentStyle.createAgentContainer}> <div className={createAgentStyle.createAgentContainer}>
<TopToolTip /> <div className={createAgentStyle.div}></div>
<div className={createAgentStyle.inputMainContainer}> <div className={createAgentStyle.inputMainContainer}>
{/* Header */} {/* AGENT FORM */}
<div className={createAgentStyle.headerContainer}> <div className={createAgentStyle.headerContainer}>
<div className={createAgentStyle.headerTxt}> <div className={createAgentStyle.headerTxt}>
<p>Agent Details</p> <p>Agent Details</p>
</div> </div>
</div> </div>
{/* Agent name input */}
<div className={createAgentStyle.inputContainer}> <div className={createAgentStyle.inputContainer}>
{/* Label */}
<div className={createAgentStyle.labelContainer}> <div className={createAgentStyle.labelContainer}>
<p>Agent Name</p> <p>Agent Name</p>
{/* <p className={createAgentStyle.required}>*</p> */} {/* <p className={createAgentStyle.required}>*</p> */}
</div> </div>
{/* Input Field */} <TextField
<TextField placeHolder="Enter agent name" /> placeHolder="Enter agent name"
{...register("agentName", { required: true })}
hasError={!!errors.agentName}
/>
<Prompts show={false} /> <Prompts show={false} />
</div> </div>
{/* Kubernetes API input */}
<div className={createAgentStyle.inputContainer}> <div className={createAgentStyle.inputContainer}>
{/* Label */}
<div className={createAgentStyle.labelContainer}> <div className={createAgentStyle.labelContainer}>
<p>Kubernetes API Proxy Name</p> <p>Kubernetes API Proxy Name</p>
{/* <p className={createAgentStyle.required}>*</p> */} {/* <p className={createAgentStyle.required}>*</p> */}
</div> </div>
{/* Input Field */} <TextField
<TextField placeHolder="Enter proxy name" /> placeHolder="Enter proxy name"
{...register("proxyName", { required: true })}
hasError={!!errors.proxyName}
/>
<Prompts show={false} /> <Prompts show={false} />
</div> </div>
{/* Checkbox */} <Checkbox
<div className={createAgentStyle.checkboxMainContainer}> checked={useVpn}
<div className={createAgentStyle.checkboxContainer}> onChange={(e) => setUseVpn(e.target.checked)}
<div className={createAgentStyle.checkbox}> label="Use Tailscale VPN"
{/* Checkbox */} description={
<div className={createAgentStyle.check}> <>
<input
type="checkbox"
checked={isChecked}
onChange={handleCheckboxChange}
className={createAgentStyle.hiddenCheckbox}
/>
</div>
{/* List */}
<div className={createAgentStyle.list}>
<p className={createAgentStyle.placeholderTxt}>
Use Tailscale VPN
</p>
<p className={createAgentStyle.secondaryTxt}>
Enable Tailscale for secure private networking. When Enable Tailscale for secure private networking. When
enabled, <br /> the endpoint will be automatically enabled, <br />
resolved from Tailscale. the endpoint will be automatically resolved from
</p> Tailscale.
</div> </>
</div> }
</div> />
</div>
{/* Agent endpoint */} {/* Agent endpoint */}
<div className={createAgentStyle.inputContainer}> <div className={createAgentStyle.inputContainer}>
{/* Label */}
<div className={createAgentStyle.labelContainer}> <div className={createAgentStyle.labelContainer}>
<p>Agent Endpoint</p> <p>Tailscale Device Prefix</p>
{/* <p className={createAgentStyle.required}>*</p> */} {/* <p className={createAgentStyle.required}>*</p> */}
</div> </div>
{/* Input Field */} <TextField
<TextField placeHolder="e.g., http://agent-01.example.com:8080" /> placeHolder="doks-onecbdev-agent"
{...register("agentEndpoint", { required: true })}
hasError={!!errors.agentEndpoint}
/>
<Prompts show={false} />
</div>
<div className={createAgentStyle.inputContainer}>
<div className={createAgentStyle.labelContainer}>
<p>Tailscale Port</p>
{/* <p className={createAgentStyle.required}>*</p> */}
</div>
<TextField
placeHolder="6969"
{...register("agentEndpoint", { required: true })}
hasError={!!errors.agentEndpoint}
/>
<Prompts show={false} /> <Prompts show={false} />
</div> </div>
</div> </div>
<div className={createAgentStyle.lokiContainer}>
{/* Header */}
<div className={createAgentStyle.headerContainer}>
<div className={createAgentStyle.headerTxt}>
<p>Loki Integration (Optional)</p>
</div> </div>
</div> </div>
<Checkbox
checked={enableLoki}
onChange={(e) => setEnableLoki(e.target.checked)}
label="Enable Loki Logging"
description="Configure Loki for centralized logging and log streaming."
/>
{enableLoki && (
<>
<div className={createAgentStyle.inputContainer}>
<div className={createAgentStyle.labelContainer}>
<p>Loki Endpoint</p>
{/* <p className={createAgentStyle.required}>*</p> */}
</div>
<TextField
placeHolder="https://loki.project-moonshot.com/"
{...register("endpoint", { required: true })}
hasError={!!errors.endpoint}
/>
<Prompts show={false} />
</div>
<div className={createAgentStyle.inputContainer}>
<div className={createAgentStyle.labelContainer}>
<p>Tenant Name</p>
{/* <p className={createAgentStyle.required}>*</p> */}
</div>
<TextField
placeHolder="doks-dev"
{...register("tenantName", { required: true })}
hasError={!!errors.tenantName}
/>
<Prompts show={false} />
</div>
<div className={createAgentStyle.inputContainer}>
<div className={createAgentStyle.labelContainer}>
<p>Password</p>
{/* <p className={createAgentStyle.required}>*</p> */}
</div>
<TextField
placeHolder="Enter Password"
{...register("password", { required: true })}
hasError={!!errors.password}
/>
<Prompts show={false} />
</div>
</>
)}
</div>
</div>
</form>
</div>
</div> </div>
</div> </div>
); );

View File

@@ -1,23 +1,31 @@
.createAgentContainer { .formContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: flex-start;
gap: 48px; gap: 10px;
flex: 1 0 0; flex: 1 0 0;
align-self: stretch; align-self: stretch;
} }
.createAgentContainer {
display: flex;
justify-content: center;
align-items: flex-start;
align-self: stretch;
gap: 16px;
flex: 1 1 auto;
padding-top: 48px;
height: calc(100vh - 100px - 70px);
overflow-y: auto;
scrollbar-width: none;
}
.inputMainContainer { .inputMainContainer {
display: flex; display: flex;
padding: 24px; padding: 24px;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 24px; gap: 24px;
border-radius: 8px;
height: calc(100vh - 260px);
position: relative;
overflow: auto;
scrollbar-width: none;
} }
.headerContainer { .headerContainer {
display: flex; display: flex;
@@ -34,9 +42,9 @@
flex: 1 0 0; flex: 1 0 0;
} }
.headerTxt > p { .headerTxt > p {
width: 163px; /* width: 163px; */
align-self: stretch;
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;
@@ -70,112 +78,11 @@
font-weight: 600; font-weight: 600;
} }
.checkboxMainContainer { .lokiContainer {
display: flex; display: flex;
width: 548px;
padding: 24px;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 10px; gap: 24px;
align-self: stretch;
}
.checkboxContainer {
display: flex;
align-items: flex-start;
align-self: stretch;
}
.checkbox {
display: flex;
padding: var(--Basic-Forms-Checkbox-Input-Sizing-Y-SM, 10px)
var(--Basic-Forms-Checkbox-Input-Sizing-X-SM, 12px);
align-items: flex-start;
gap: var(--Basic-Forms-Checkbox-Gap-Between, 16px);
flex: 1 0 0;
border-radius: var(--Basic-Forms-Checkbox-Border-Radius, 8px);
background: #1d1e2a;
background: color(display-p3 0.1137 0.1176 0.1608);
}
.check {
display: flex;
padding-top: var(--Basic-Forms-Checkbox-Inline-Y, 4px);
align-items: flex-start;
gap: var(--Basic-Forms-Checkbox-Inline-Gap-Between, 10px);
}
.hiddenCheckbox {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--Basic-Forms-Checkbox-Check-Icon-Border-Radius, 4px);
border: 1px solid
var(--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color, #0067fd);
border: 1px solid
var(
--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
cursor: pointer;
position: relative;
}
.hiddenCheckbox:checked {
border-radius: var(--Basic-Forms-Checkbox-Check-Icon-Border-Radius, 4px);
border: 1px solid
var(--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color, #0067fd);
border: 1px solid
var(
--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
background: var(
--Basic-Forms-Checkbox-Check-Icon-Background-Bg-Active-Color,
#0067fd
);
background: var(
--Basic-Forms-Checkbox-Check-Icon-Background-Bg-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
}
.hiddenCheckbox::after {
content: "";
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='8' viewBox='0 0 11 8' fill='none'%3E%3Cpath d='M9.83333 0.5L3.41667 6.91667L0.5 4' stroke='white' style='stroke:white;stroke-opacity:1;' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
opacity: 0;
}
.hiddenCheckbox:checked::after {
opacity: 1;
}
.list {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
flex: 1 0 0;
}
.placeholderTxt {
align-self: stretch;
color: #d2d3e1;
color: color(display-p3 0.8235 0.8275 0.8784);
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 24px; /* 150% */
letter-spacing: 0.08px;
}
.secondaryTxt {
align-self: stretch;
color: #697281;
color: color(display-p3 0.4196 0.4471 0.502);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 142.857% */
letter-spacing: 0.07px;
} }

View File

@@ -8,13 +8,11 @@ import createAgentStyle from "./styles.module.css";
import Prompts from "@/app/components/prompts/Prompts"; import Prompts from "@/app/components/prompts/Prompts";
import useAgentForm from "@/app/hooks/useAgentForm"; import useAgentForm from "@/app/hooks/useAgentForm";
import Alert from "@/app/components/alerts/Alert"; import Alert from "@/app/components/alerts/Alert";
import Checkbox from "@/app/components/customCheckbox/Checkbox";
const AddAgentPage = () => { const AddAgentPage = () => {
const [isChecked, setIsChecked] = useState(false); const [useVpn, setUseVpn] = useState(false);
const [enableLoki, setEnableLoki] = useState(false);
const handleCheckboxChange = (e) => {
setIsChecked(e.target.checked);
};
const { const {
register, register,
handleSubmit, handleSubmit,
@@ -42,28 +40,27 @@ const AddAgentPage = () => {
state="add" state="add"
requiredButtons={["title", "save"]} requiredButtons={["title", "save"]}
/> />
{/* Create agent Container */}
<div className={createAgentStyle.createAgentContainer}>
<TopToolTip /> <TopToolTip />
{/* Create agent Container */}
<form <form
className={createAgentStyle.inputMainContainer} className={createAgentStyle.formContainer}
id="form" id="form"
onSubmit={handleSubmit(onSubmit)} onSubmit={handleSubmit(onSubmit)}
> >
{/* Header */} <div className={createAgentStyle.createAgentContainer}>
<div className={createAgentStyle.div}></div>
<div className={createAgentStyle.inputMainContainer}>
{/* AGENT FORM */}
<div className={createAgentStyle.headerContainer}> <div className={createAgentStyle.headerContainer}>
<div className={createAgentStyle.headerTxt}> <div className={createAgentStyle.headerTxt}>
<p>Agent Details</p> <p>Agent Details</p>
</div> </div>
</div> </div>
{/* Agent name input */}
<div className={createAgentStyle.inputContainer}> <div className={createAgentStyle.inputContainer}>
{/* Label */}
<div className={createAgentStyle.labelContainer}> <div className={createAgentStyle.labelContainer}>
<p>Agent Name</p> <p>Agent Name</p>
<p className={createAgentStyle.required}>*</p> <p className={createAgentStyle.required}>*</p>
</div> </div>
{/* Input Field */}
<TextField <TextField
placeHolder="Enter agent name" placeHolder="Enter agent name"
{...register("agentName", { required: true })} {...register("agentName", { required: true })}
@@ -71,14 +68,11 @@ const AddAgentPage = () => {
/> />
<Prompts show={false} /> <Prompts show={false} />
</div> </div>
{/* Kubernetes API input */}
<div className={createAgentStyle.inputContainer}> <div className={createAgentStyle.inputContainer}>
{/* Label */}
<div className={createAgentStyle.labelContainer}> <div className={createAgentStyle.labelContainer}>
<p>Kubernetes API Proxy Name</p> <p>Kubernetes API Proxy Name</p>
<p className={createAgentStyle.required}>*</p> <p className={createAgentStyle.required}>*</p>
</div> </div>
{/* Input Field */}
<TextField <TextField
placeHolder="Enter proxy name" placeHolder="Enter proxy name"
{...register("proxyName", { required: true })} {...register("proxyName", { required: true })}
@@ -86,41 +80,25 @@ const AddAgentPage = () => {
/> />
<Prompts show={false} /> <Prompts show={false} />
</div> </div>
{/* Checkbox */} <Checkbox
<div className={createAgentStyle.checkboxMainContainer}> checked={useVpn}
<div className={createAgentStyle.checkboxContainer}> onChange={(e) => setUseVpn(e.target.checked)}
<div className={createAgentStyle.checkbox}> label="Use Tailscale VPN"
{/* Checkbox */} description={
<div className={createAgentStyle.check}> <>
<input
type="checkbox"
checked={isChecked}
onChange={handleCheckboxChange}
className={createAgentStyle.hiddenCheckbox}
/>
</div>
{/* List */}
<div className={createAgentStyle.list}>
<p className={createAgentStyle.placeholderTxt}>
Use Tailscale VPN
</p>
<p className={createAgentStyle.secondaryTxt}>
Enable Tailscale for secure private networking. When Enable Tailscale for secure private networking. When
enabled, <br /> the endpoint will be automatically enabled, <br />
resolved from Tailscale. the endpoint will be automatically resolved from
</p> Tailscale.
</div> </>
</div> }
</div> />
</div>
{/* Agent endpoint */} {/* Agent endpoint */}
<div className={createAgentStyle.inputContainer}> <div className={createAgentStyle.inputContainer}>
{/* Label */}
<div className={createAgentStyle.labelContainer}> <div className={createAgentStyle.labelContainer}>
<p>Agent Endpoint</p> <p>Agent Endpoint</p>
<p className={createAgentStyle.required}>*</p> <p className={createAgentStyle.required}>*</p>
</div> </div>
{/* Input Field */}
<TextField <TextField
placeHolder="e.g., http://agent-01.example.com:8080" placeHolder="e.g., http://agent-01.example.com:8080"
{...register("agentEndpoint", { required: true })} {...register("agentEndpoint", { required: true })}
@@ -128,8 +106,65 @@ const AddAgentPage = () => {
/> />
<Prompts show={false} /> <Prompts show={false} />
</div> </div>
</form>
</div> </div>
<div className={createAgentStyle.lokiContainer}>
{/* Header */}
<div className={createAgentStyle.headerContainer}>
<div className={createAgentStyle.headerTxt}>
<p>Loki Integration (Optional)</p>
</div>
</div>
<Checkbox
checked={enableLoki}
onChange={(e) => setEnableLoki(e.target.checked)}
label="Enable Loki Logging"
description="Configure Loki for centralized logging and log streaming."
/>
{enableLoki && (
<>
<div className={createAgentStyle.inputContainer}>
<div className={createAgentStyle.labelContainer}>
<p>Loki Endpoint</p>
{/* <p className={createAgentStyle.required}>*</p> */}
</div>
<TextField
placeHolder="https://loki.project-moonshot.com/"
{...register("endpoint", { required: true })}
hasError={!!errors.endpoint}
/>
<Prompts show={false} />
</div>
<div className={createAgentStyle.inputContainer}>
<div className={createAgentStyle.labelContainer}>
<p>Tenant Name</p>
{/* <p className={createAgentStyle.required}>*</p> */}
</div>
<TextField
placeHolder="doks-dev"
{...register("tenantName", { required: true })}
hasError={!!errors.tenantName}
/>
<Prompts show={false} />
</div>
<div className={createAgentStyle.inputContainer}>
<div className={createAgentStyle.labelContainer}>
<p>Password</p>
{/* <p className={createAgentStyle.required}>*</p> */}
</div>
<TextField
placeHolder="Enter Password"
{...register("password", { required: true })}
hasError={!!errors.password}
/>
<Prompts show={false} />
</div>
</>
)}
</div>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,24 +1,32 @@
.createAgentContainer { .formContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: flex-start;
gap: 48px; gap: 10px;
flex: 1 0 0; flex: 1 0 0;
padding-top: 48px;
align-self: stretch; align-self: stretch;
} }
.createAgentContainer {
display: flex;
justify-content: center;
align-items: flex-start;
align-self: stretch;
/* Ibalhin ni ang padding sa formcontainer maoang nasa figma */
gap: 16px;
flex: 1 1 auto;
height: calc(100vh - 100px - 48px - 110px);
overflow-y: auto;
scrollbar-width: none;
}
.inputMainContainer { .inputMainContainer {
display: flex; display: flex;
padding: 24px; padding: 24px;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 24px; gap: 24px;
border-radius: 8px;
/* Remove ni kung mo ingon si sir grills */
height: calc(100vh - 260px);
position: relative;
overflow: auto;
scrollbar-width: none;
} }
.headerContainer { .headerContainer {
display: flex; display: flex;
@@ -35,9 +43,9 @@
flex: 1 0 0; flex: 1 0 0;
} }
.headerTxt > p { .headerTxt > p {
width: 163px; /* width: 163px; */
align-self: stretch;
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;
@@ -71,112 +79,11 @@
font-weight: 600; font-weight: 600;
} }
.checkboxMainContainer { .lokiContainer {
display: flex; display: flex;
width: 548px;
padding: 24px;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 10px; gap: 24px;
align-self: stretch;
}
.checkboxContainer {
display: flex;
align-items: flex-start;
align-self: stretch;
}
.checkbox {
display: flex;
padding: var(--Basic-Forms-Checkbox-Input-Sizing-Y-SM, 10px)
var(--Basic-Forms-Checkbox-Input-Sizing-X-SM, 12px);
align-items: flex-start;
gap: var(--Basic-Forms-Checkbox-Gap-Between, 16px);
flex: 1 0 0;
border-radius: var(--Basic-Forms-Checkbox-Border-Radius, 8px);
background: #1d1e2a;
background: color(display-p3 0.1137 0.1176 0.1608);
}
.check {
display: flex;
padding-top: var(--Basic-Forms-Checkbox-Inline-Y, 4px);
align-items: flex-start;
gap: var(--Basic-Forms-Checkbox-Inline-Gap-Between, 10px);
}
.hiddenCheckbox {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--Basic-Forms-Checkbox-Check-Icon-Border-Radius, 4px);
border: 1px solid
var(--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color, #0067fd);
border: 1px solid
var(
--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
cursor: pointer;
position: relative;
}
.hiddenCheckbox:checked {
border-radius: var(--Basic-Forms-Checkbox-Check-Icon-Border-Radius, 4px);
border: 1px solid
var(--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color, #0067fd);
border: 1px solid
var(
--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
background: var(
--Basic-Forms-Checkbox-Check-Icon-Background-Bg-Active-Color,
#0067fd
);
background: var(
--Basic-Forms-Checkbox-Check-Icon-Background-Bg-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
}
.hiddenCheckbox::after {
content: "";
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='8' viewBox='0 0 11 8' fill='none'%3E%3Cpath d='M9.83333 0.5L3.41667 6.91667L0.5 4' stroke='white' style='stroke:white;stroke-opacity:1;' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
opacity: 0;
}
.hiddenCheckbox:checked::after {
opacity: 1;
}
.list {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
flex: 1 0 0;
}
.placeholderTxt {
align-self: stretch;
color: #d2d3e1;
color: color(display-p3 0.8235 0.8275 0.8784);
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 24px; /* 150% */
letter-spacing: 0.08px;
}
.secondaryTxt {
align-self: stretch;
color: #697281;
color: color(display-p3 0.4196 0.4471 0.502);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 142.857% */
letter-spacing: 0.07px;
} }

View File

@@ -0,0 +1,32 @@
import React from "react";
import styles from "./styles.module.css";
const Checkbox = ({ checked, onChange, label, description }) => {
return (
<>
{/* Checkbox */}
<div className={styles.checkboxMainContainer}>
<div className={styles.checkboxContainer}>
<div className={styles.checkbox}>
{/* Checkbox */}
<div className={styles.check}>
<input
type="checkbox"
checked={checked}
onChange={onChange}
className={styles.hiddenCheckbox}
/>
</div>
{/* List */}
<div className={styles.list}>
<p className={styles.placeholderTxt}>{label}</p>
<p className={styles.secondaryTxt}>{description}</p>
</div>
</div>
</div>
</div>
</>
);
};
export default Checkbox;

View File

@@ -0,0 +1,105 @@
.checkboxMainContainer {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
align-self: stretch;
}
.checkboxContainer {
display: flex;
align-items: flex-start;
align-self: stretch;
}
.checkbox {
display: flex;
padding: var(--Basic-Forms-Checkbox-Input-Sizing-Y-SM, 10px)
var(--Basic-Forms-Checkbox-Input-Sizing-X-SM, 12px);
align-items: flex-start;
gap: var(--Basic-Forms-Checkbox-Gap-Between, 16px);
flex: 1 0 0;
border-radius: var(--Basic-Forms-Checkbox-Border-Radius, 8px);
background: #1d1e2a;
background: color(display-p3 0.1137 0.1176 0.1608);
}
.check {
display: flex;
padding-top: var(--Basic-Forms-Checkbox-Inline-Y, 4px);
align-items: flex-start;
gap: var(--Basic-Forms-Checkbox-Inline-Gap-Between, 10px);
}
.hiddenCheckbox {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
border-radius: var(--Basic-Forms-Checkbox-Check-Icon-Border-Radius, 4px);
border: 1px solid
var(--Basic-Forms-Checkbox-Check-Icon-Border-Border-Color, #e5e7eb);
background: var(--Basic-Forms-Checkbox-Check-Icon-Background-Bg-Color, #fff);
}
.hiddenCheckbox:checked {
border-radius: var(--Basic-Forms-Checkbox-Check-Icon-Border-Radius, 4px);
border: 1px solid
var(--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color, #0067fd);
border: 1px solid
var(
--Basic-Forms-Checkbox-Check-Icon-Border-Border-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
background: var(
--Basic-Forms-Checkbox-Check-Icon-Background-Bg-Active-Color,
#0067fd
);
background: var(
--Basic-Forms-Checkbox-Check-Icon-Background-Bg-Active-Color,
color(display-p3 0.1451 0.3882 0.9216)
);
}
.hiddenCheckbox::after {
content: "";
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='8' viewBox='0 0 11 8' fill='none'%3E%3Cpath d='M9.83333 0.5L3.41667 6.91667L0.5 4' stroke='white' style='stroke:white;stroke-opacity:1;' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
opacity: 0;
}
.hiddenCheckbox:checked::after {
opacity: 1;
}
.list {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
flex: 1 0 0;
}
.placeholderTxt {
align-self: stretch;
color: #d2d3e1;
color: color(display-p3 0.8235 0.8275 0.8784);
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 24px; /* 150% */
letter-spacing: 0.08px;
}
.secondaryTxt {
align-self: stretch;
color: #697281;
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 142.857% */
letter-spacing: 0.07px;
}

View File

@@ -6,7 +6,6 @@ import useIsMobile from "@/app/hooks/useIsMobile";
const Profile = () => { const Profile = () => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [user, setUser] = useState("");
const sampleData = [ const sampleData = [
{ {
name: "JM Grills", name: "JM Grills",

View File

@@ -22,7 +22,6 @@
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);
} }
.header { .header {
display: flex; display: flex;

View File

@@ -0,0 +1,28 @@
import DeleteIcon from "@/app/components/icons/delete";
import React from "react";
const Card = (props) => {
return (
<div className={styles.cardContainer} {...props}>
<div className={styles.cardDetails}>
<div className={styles.list}>
<p>Name</p>
<p>{props?.user?.email}</p>
</div>
<div className={styles.list}>
<p>Organization ID</p>
<p>{props?.user?.fullName}</p>
</div>
<div className={styles.list}>
<p>Permissions</p>
<p>{props?.user?.createdAt}</p>
</div>
</div>
<div className={styles.cardAction}>
<DeleteIcon />
</div>
</div>
);
};
export default Card;

View File

@@ -0,0 +1,43 @@
.cardContainer {
display: flex;
padding: 16px 0;
align-items: flex-start;
gap: 16px;
align-self: stretch;
border-bottom: 1px solid #2c2d3d;
cursor: pointer;
}
.cardDetails {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
flex: 1 0 0;
}
.list {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 5px;
align-self: stretch;
}
.list p {
color: #85869b;
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.list p:nth-child(2) {
color: #eeeffd;
font-family: Inter;
font-size: 16px;
font-weight: 500;
}
.cardAction {
display: flex;
align-items: center;
gap: 17px;
}

View File

@@ -22,7 +22,6 @@
padding: 12px 24px; padding: 12px 24px;
text-align: left; text-align: left;
color: #85869b; color: #85869b;
color: color(display-p3 0.5216 0.5255 0.6);
font-family: Inter; font-family: Inter;
font-size: 13px; font-size: 13px;
font-style: normal; font-style: normal;
@@ -41,7 +40,6 @@
.tableBody td { .tableBody td {
padding: 12px 24px; padding: 12px 24px;
color: #eeeffd; color: #eeeffd;
color: color(display-p3 0.9333 0.9373 0.9882);
font-family: Inter; font-family: Inter;
font-size: 13px; font-size: 13px;
font-style: normal; font-style: normal;
@@ -55,9 +53,7 @@
} }
.tableBody tr:hover { .tableBody tr: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);
} }
.tableActions { .tableActions {
@@ -68,3 +64,16 @@
gap: 12px; gap: 12px;
align-self: stretch; align-self: stretch;
} }
/* Mobile */
.cardContainer {
display: flex;
padding: 0 16px;
padding-bottom: 50px;
height: calc(100vh - 170px);
flex-direction: column;
align-items: flex-start;
gap: 12px;
align-self: stretch;
overflow: auto;
}

View File

@@ -8,25 +8,25 @@ 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 useIsMobile from "../hooks/useIsMobile";
import MobileSearchBar from "../components/mobileSearchBar/MobileSearchBar";
import Card from "./user-card/Card";
const UsersPage = () => { const UsersPage = () => {
const router = useRouter(); const router = useRouter();
const isMobile = useIsMobile();
const sampleData = [ const sampleData = [
{ {
id: 1,
email: "nino.moonshot@gmail.com", email: "nino.moonshot@gmail.com",
fullName: "Nino Paul Cervantes", fullName: "Nino Paul Cervantes",
createdAt: "2024-10-21 08:01:31.474 +0000 UTC", createdAt: "2024-10-21 08:01:31.474 +0000 UTC",
}, },
{ {
id: 2,
email: "nino.moonshot@gmail.com", email: "nino.moonshot@gmail.com",
fullName: "Nino Paul Cervantes", fullName: "Nino Paul Cervantes",
createdAt: "2024-10-21 08:01:31.474 +0000 UTC", createdAt: "2024-10-21 08:01:31.474 +0000 UTC",
}, },
{ {
id: 3,
email: "nino.moonshot@gmail.com", email: "nino.moonshot@gmail.com",
fullName: "Nino Paul Cervantes", fullName: "Nino Paul Cervantes",
createdAt: "2024-10-21 08:01:31.474 +0000 UTC", createdAt: "2024-10-21 08:01:31.474 +0000 UTC",
@@ -42,6 +42,20 @@ const UsersPage = () => {
topbarTitle="Users" topbarTitle="Users"
requiredButtons={["title", "add", "search"]} requiredButtons={["title", "add", "search"]}
/> />
<div className={styles.cardContainer}>
<MobileSearchBar />
{sampleData.map((user, index) => {
return (
<Card
user={user}
key={index}
onClick={() => router.push(`/users/${index}`)}
/>
);
})}
</div>
<div className={styles.tableContainer}> <div className={styles.tableContainer}>
<table className={styles.table}> <table className={styles.table}>
<thead className={styles.tableHeader}> <thead className={styles.tableHeader}>
@@ -56,8 +70,8 @@ const UsersPage = () => {
{sampleData.map((user, index) => { {sampleData.map((user, index) => {
return ( return (
<tr <tr
key={user.id} key={index}
onClick={() => router.push(`/users/${user.id}`)} onClick={() => router.push(`/users/${index}`)}
> >
<td>{user.email}</td> <td>{user.email}</td>
<td>{user.fullName}</td> <td>{user.fullName}</td>

View File

@@ -4,7 +4,6 @@
position: relative; position: relative;
overflow: auto; overflow: auto;
} }
/* Table */ /* Table */
.table { .table {
width: 100%; width: 100%;
@@ -22,9 +21,9 @@
padding: 12px 24px; padding: 12px 24px;
text-align: left; text-align: left;
color: #85869b; color: #85869b;
color: color(display-p3 0.5216 0.5255 0.6);
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;
@@ -40,9 +39,8 @@
.tableBody td { .tableBody td {
padding: 12px 24px; padding: 12px 24px;
color: #eeeffd; color: #eeeffd;
color: color(display-p3 0.9333 0.9373 0.9882);
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;
@@ -54,9 +52,7 @@
} }
.tableBody tr:hover { .tableBody tr: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);
} }
.tableActions { .tableActions {
@@ -67,3 +63,24 @@
gap: 12px; gap: 12px;
align-self: stretch; align-self: stretch;
} }
.cardContainer {
display: none;
}
@media (max-width: 768px) {
.cardContainer {
display: flex;
padding: 0 16px;
padding-bottom: 50px;
height: calc(100vh - 170px);
flex-direction: column;
align-items: flex-start;
gap: 12px;
align-self: stretch;
overflow: auto;
background-color: red;
}
.tableContainer {
display: none;
}
}

View File

@@ -0,0 +1,29 @@
import DeleteIcon from "@/app/components/icons/delete";
import React from "react";
import styles from "./styles.module.css";
const Card = (props) => {
return (
<div className={styles.cardContainer} {...props}>
<div className={styles.cardDetails}>
<div className={styles.list}>
<p>Email</p>
<p>{props?.user?.email}</p>
</div>
<div className={styles.list}>
<p>Full Name</p>
<p>{props?.user?.fullName}</p>
</div>
<div className={styles.list}>
<p>Date Created</p>
<p>{props?.user?.createdAt}</p>
</div>
</div>
<div className={styles.cardAction}>
<DeleteIcon />
</div>
</div>
);
};
export default Card;

View File

@@ -0,0 +1,44 @@
.cardContainer {
display: flex;
padding: 16px 0;
align-items: flex-start;
gap: 16px;
align-self: stretch;
border-bottom: 1px solid #2c2d3d;
cursor: pointer;
background-color: green;
}
.cardDetails {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
flex: 1 0 0;
}
.list {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 5px;
align-self: stretch;
}
.list p {
color: #85869b;
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.list p:nth-child(2) {
color: #eeeffd;
font-family: Inter;
font-size: 16px;
font-weight: 500;
}
.cardAction {
display: flex;
align-items: center;
gap: 17px;
}