This commit is contained in:
npalOmega 2026-01-13 12:03:46 -06:00
parent ff2eacaba4
commit 3f3dcc70f6
6 changed files with 244 additions and 128 deletions

View file

@ -305,3 +305,57 @@ body {
.contactTable tbody tr:nth-child(even) {
background: #fbfbfb;
}
.controlsTop {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
}
.leftControls {
display: flex;
align-items: center;
}
.filter {
display: flex;
align-items: center;
}
.filter select {
height: 34px;
border: 1px solid #ccc;
border-right: none;
border-radius: 4px 0 0 4px;
padding: 6px 8px;
background: #fff;
}
.filter input {
height: 34px;
width: 260px;
border: 1px solid #ccc;
border-left: none;
border-right: none;
padding: 6px 10px;
}
.searchBtn {
height: 34px;
padding: 0 14px;
border: 1px solid #0073aa;
background-color: #0073aa;
color: #fff;
border-radius: 0 4px 4px 0;
cursor: pointer;
}
.searchBtn:hover {
background-color: #005f8d;
}
.rightControls {
display: flex;
align-items: center;
}

View file

@ -1,52 +1,100 @@
import React, { useEffect, useState } from "react";
import { fetchAccountsPaged, deleteAccount } from "./api";
import { useNavigate } from "react-router";
import "./styles.css";
export default function AccountList() {
const [data, setData] = useState([]);
const navigate = useNavigate();
const [rows, setRows] = useState([]);
const [search, setSearch] = useState("");
const [loading, setLoading] = useState(true);
const load = async () => {
setLoading(true);
const res = await fetchAccountsPaged({ search });
setRows(res?.rows ?? []);
setLoading(false);
};
useEffect(() => {
load();
}, []);
}, [search]);
const load = async () => {
const res = await fetchAccountsPaged({});
setData(res.rows);
const handleDelete = async (id) => {
if (!window.confirm("Delete this account?")) return;
await deleteAccount(id);
load();
};
return (
<div>
<h2>Accounts</h2>
<section className="contactsPage">
<div className="pageTitle">Accounts</div>
<button onClick={() => (window.location.href = "/accounts/new")}>
+ Add Account
</button>
<div className="card controlsCard">
<div className="controlsTop">
<div className="leftControls">
<div className="filter">
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Enter search criteria here..."
/>
<button className="searchBtn">Search!</button>
</div>
</div>
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Industry</th>
<th>Phone</th>
<th></th>
</tr>
</thead>
<div className="rightControls">
<button
className="iconSmall"
title="New Account"
onClick={() => navigate("/accounts/new")}
>
+
</button>
</div>
</div>
<tbody>
{data.map((x) => (
<tr key={x.id}>
<td>{x.name}</td>
<td>{x.industry}</td>
<td>{x.phone}</td>
<td>
<button onClick={() => (window.location.href = `/accounts/${x.id}/edit`)}>
Edit
</button>
<button onClick={() => deleteAccount(x.id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
{loading ? (
<div style={{ padding: 12 }}>Loading accounts...</div>
) : (
<table className="contactTable">
<thead>
<tr>
<th>Name</th>
<th>Industry</th>
<th>Phone</th>
<th></th>
</tr>
</thead>
<tbody>
{rows.map((x) => (
<tr key={x.id}>
<td>{x.name}</td>
<td>{x.industry}</td>
<td>{x.phoneNumber}</td>
<td className="iconActions">
<button
className="iconBtn"
title="Edit"
onClick={() => navigate(`/accounts/${x.id}/edit`)}
>
✏️
</button>
<button
className="iconBtnDanger"
title="Delete"
onClick={() => handleDelete(x.id)}
>
🗑
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</section>
);
}

View file

@ -12,23 +12,25 @@ export const fetchAccountsPaged = async ({
)}&page=${page}&pageSize=${pageSize}`
);
const rows = (res?.data || res?.Data || []).map((x) => ({
id: x.Id,
name: x.Name ?? x.AccountName ?? "",
number: x.AccountNumber ?? "",
industry: x.Industry ?? "",
website: x.Website ?? "",
primaryContact: x.PrimaryContact ?? "",
phoneNumber: x.Phone ?? x.PhoneNumber ?? "",
owner: x.Owner ?? "",
const data = res?.data || res?.Data || [];
const rows = data.map((x) => ({
id: x.id ?? x.Id,
name: x.name ?? x.Name ?? x.AccountName ?? "",
number: x.number ?? x.AccountNumber ?? "",
industry: x.industry ?? x.Industry ?? "",
website: x.website ?? x.Website ?? "",
primaryContact: x.primaryContact ?? x.PrimaryContact ?? "",
phoneNumber: x.phoneNumber ?? x.Phone ?? x.PhoneNumber ?? "",
owner: x.owner ?? x.Owner ?? "",
}));
return {
rows,
page: res?.PageNumber ?? 1,
pageSize: res?.PageSize ?? pageSize,
totalCount: res?.TotalCount ?? rows.length,
totalPages: res?.TotalPages ?? 1,
page: res?.page ?? res?.PageNumber ?? page,
pageSize: res?.pageSize ?? res?.PageSize ?? pageSize,
totalCount: res?.totalCount ?? res?.TotalCount ?? rows.length,
totalPages: res?.totalPages ?? res?.TotalPages ?? 1,
};
};

View file

@ -5,20 +5,20 @@ import { API_URL } from '../../../constants';
export const fetchContactsPaged = async ({ search = "", page = 1, pageSize = 12 }) => {
const res = await get(`${API_URL}/Contact/GetContactList?search=${encodeURIComponent(search)}&page=${page}&pageSize=${pageSize}`);
const rows = (res?.data || res?.Data || []).map(x => ({
id: x.Id,
name: x.Name,
account: x.Account ?? "",
title: x.Title ?? "",
email: x.email ?? x.Email ?? "",
phoneNumber: x.phone ?? x.PhoneNumber ?? "",
owner: x.Owner ?? "",
id: x.id,
name: x.name,
account: x.account ?? "",
title: x.title ?? "",
email: x.email ?? "",
phoneNumber: x.phone ?? "",
owner: x.owner ?? "",
}));
return {
rows,
page: res?.PageNumber ?? 1,
pageSize: res?.PageSize ?? pageSize,
totalCount: res?.TotalCount ?? rows.length,
totalPages: res?.TotalPages ?? 1,
page: res?.pageNumber ?? 1,
pageSize: res?.pageSize ?? pageSize,
totalCount: res?.totalCount ?? rows.length,
totalPages: res?.totalPages ?? 1,
};
};

View file

@ -1,18 +1,18 @@
import React, { useEffect, useState } from "react";
import { fetchEmployeesPaged, deleteEmployee } from "./api";
import { useNavigate } from "react-router";
import { toast } from "react-toastify";
import "./styles.css";
export default function EmployeeList() {
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [rows, setRows] = useState([]);
const [search, setSearch] = useState("");
const [loading, setLoading] = useState(true);
const load = async () => {
setLoading(true);
const res = await fetchEmployeesPaged({ search });
setRows(res.rows);
setRows(res?.rows ?? []);
setLoading(false);
};
@ -23,70 +23,82 @@ export default function EmployeeList() {
const handleDelete = async (id) => {
if (!window.confirm("Delete this employee?")) return;
await deleteEmployee(id);
toast.success("Employee deleted");
load();
};
return (
<div className="pageWrapper">
<div className="pageHeader">
<h2>Employees</h2>
<button onClick={() => navigate("/employees/new")} className="btnPrimary">
+ New Employee
</button>
</div>
<section className="contactsPage">
<div className="pageTitle">Employees</div>
<input
className="searchBoxList"
placeholder="Search employees..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<div className="card controlsCard">
<div className="controlsTop">
<div className="leftControls">
<div className="filter">
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Enter search criteria here..."
/>
<button className="searchBtn">Search!</button>
</div>
</div>
{loading ? (
<p>Loading employees...</p>
) : (
<table className="dataTable">
<thead>
<tr>
<th>Name</th>
<th>Job Title</th>
<th>Email</th>
<th>Phone</th>
<th>Department</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<div className="rightControls">
<button
className="iconSmall"
title="New Employee"
onClick={() => navigate("/employees/new")}
>
+
</button>
</div>
</div>
<tbody>
{rows.map((r) => (
<tr key={r.id}>
<td>{r.name}</td>
<td>{r.jobTitle}</td>
<td>{r.email}</td>
<td>{r.phone}</td>
<td>{r.department}</td>
<td>{r.status}</td>
<td>
<button
className="btnSmall"
onClick={() => navigate(`/employees/${r.id}`)}
>
Edit
</button>
<button
className="btnSmallDanger"
onClick={() => handleDelete(r.id)}
>
Del
</button>
</td>
{loading ? (
<div style={{ padding: 12 }}>Loading employees...</div>
) : (
<table className="contactTable">
<thead>
<tr>
<th>Name</th>
<th>Job Title</th>
<th>Email</th>
<th>Phone</th>
<th>Status</th>
<th></th>
</tr>
))}
</tbody>
</table>
)}
</div>
</thead>
<tbody>
{rows.map((r) => (
<tr key={r.id}>
<td>{r.name}</td>
<td>{r.jobTitle}</td>
<td>{r.email}</td>
<td>{r.phone}</td>
<td>{r.status}</td>
<td className="iconActions">
<button
className="iconBtn"
title="Edit"
onClick={() => navigate(`/employees/${r.id}`)}
>
✏️
</button>
<button
className="iconBtnDanger"
title="Delete"
onClick={() => handleDelete(r.id)}
>
🗑
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</section>
);
}

View file

@ -12,14 +12,14 @@ export const fetchEmployeesPaged = async ({
)}&page=${page}&pageSize=${pageSize}`
);
const rows = (res?.data || res?.Data || []).map((x) => ({
id: x.Id,
name: `${x.FirstName ?? ""} ${x.LastName ?? ""}`.trim(),
jobTitle: x.JobTitle ?? "",
email: x.Email ?? "",
phone: x.Phone ?? "",
department: x.Department ?? "",
status: x.Status ?? "",
const rows = (res || []).map((x) => ({
id: x.id,
name: `${x.name ?? ""} ${x.name ?? ""}`.trim(),
jobTitle: x.jobTitle ?? "",
email: x.email ?? "",
phone: x.phone ?? "",
department: x.department ?? "",
status: x.status ?? "",
}));
return {