mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
feat(ui):
This commit is contained in:
@@ -76,6 +76,12 @@ SEED_EDR_ORG=true
|
||||
SEED_FREIGHT_STAFF=true
|
||||
SEED_EXPORT_DJIBOUTI_INTERCHANGE_DEMO=false
|
||||
|
||||
# Limits GET /staff/users to employees of this IAM organization (iam.organizations.key).
|
||||
# Unset = every employee. A key matching no organization returns no users.
|
||||
# Dev seed key: edr_freight
|
||||
# Production: ETHIO_DJIBOUTI_STANDARD_GAUGE_RAILWAY_SHARE_COMPANY_001
|
||||
FREIGHT_ORG_KEY=edr_freight
|
||||
|
||||
# MinIO (used by @tria-plc/iamapi-common for file storage)
|
||||
MINIO_ENDPOINT=localhost
|
||||
MINIO_PORT=9000
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { PaginatedResponse } from '@edr/types';
|
||||
import { User } from '@tria-plc/iamapi-common/entities/iam/user/user.entity';
|
||||
@@ -19,6 +20,7 @@ import { paginateQuery } from '../../common/utils/pagination.util';
|
||||
export class ListUsersService {
|
||||
constructor(
|
||||
@InjectRepository(User) private readonly users: Repository<User>,
|
||||
private readonly config: ConfigService,
|
||||
) {}
|
||||
|
||||
findAll(query: ListUsersQueryDto): Promise<PaginatedResponse<User>> {
|
||||
@@ -40,6 +42,29 @@ export class ListUsersService {
|
||||
])
|
||||
.orderBy(`user.${sortBy}`, query.sortOrder ?? 'ASC');
|
||||
|
||||
// Restrict to one IAM organization when configured. The org key differs per
|
||||
// environment (dev seeds `edr_freight`, production uses the registered
|
||||
// company key), so this is config rather than a constant. An unset key
|
||||
// means no restriction; a key matching no organization matches no user —
|
||||
// failing closed rather than silently widening to every org.
|
||||
const orgKey = this.config.get<string>('FREIGHT_ORG_KEY');
|
||||
if (orgKey) {
|
||||
// EXISTS, not a join: a user with several employee rows would otherwise
|
||||
// be returned once per row, duplicating them in the list and inflating
|
||||
// `getManyAndCount`'s total.
|
||||
qb.andWhere(
|
||||
`EXISTS (
|
||||
SELECT 1
|
||||
FROM iam.employees emp
|
||||
JOIN iam.organizations org ON org.id = emp.organization_id
|
||||
WHERE emp.user_id = "user".id
|
||||
AND org.key = :orgKey
|
||||
AND org.deleted_at IS NULL
|
||||
)`,
|
||||
{ orgKey },
|
||||
);
|
||||
}
|
||||
|
||||
if (query.userType) {
|
||||
qb.andWhere('user.userType = :userType', { userType: query.userType });
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ const RuleEngineCardGrid = ({
|
||||
config={config}
|
||||
layout="compact"
|
||||
readOnly={readOnly}
|
||||
onEdit={onEdit ?? (() => { })}
|
||||
onEdit={onEdit}
|
||||
onDelete={onDelete ?? (() => { })}
|
||||
onViewChain={onViewChain}
|
||||
onSubmitRate={onSubmitRate}
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { RuleEngineRecord } from "@/types/rule-engine";
|
||||
export interface RuleEngineRecordActionsProps {
|
||||
record: RuleEngineRecord;
|
||||
config: RuleEngineResourceConfig;
|
||||
onEdit: (record: RuleEngineRecord) => void;
|
||||
onEdit?: (record: RuleEngineRecord) => void;
|
||||
onDelete: (record: RuleEngineRecord) => void;
|
||||
onViewChain?: () => void;
|
||||
onSubmitRate?: (id: string) => void;
|
||||
@@ -93,17 +93,19 @@ const RuleEngineRecordActions = ({
|
||||
) : null}
|
||||
|
||||
<div style={actionGroupStyle}>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
size="compact-sm"
|
||||
radius="md"
|
||||
onClick={() => onEdit(record)}
|
||||
leftSection={<Pencil size={14} />}
|
||||
styles={{ root: { fontWeight: 600 } }}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
{onEdit ? (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
size="compact-sm"
|
||||
radius="md"
|
||||
onClick={() => onEdit(record)}
|
||||
leftSection={<Pencil size={14} />}
|
||||
styles={{ root: { fontWeight: 600 } }}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="red"
|
||||
@@ -151,21 +153,23 @@ const RuleEngineRecordActions = ({
|
||||
) : null}
|
||||
|
||||
<div style={actionGroupStyle}>
|
||||
<Tooltip label="Edit">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
size="md"
|
||||
radius="md"
|
||||
onClick={() => onEdit(record)}
|
||||
aria-label="Edit record"
|
||||
style={{
|
||||
background: "white",
|
||||
}}
|
||||
>
|
||||
<Pencil size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
{onEdit ? (
|
||||
<Tooltip label="Edit">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
size="md"
|
||||
radius="md"
|
||||
onClick={() => onEdit(record)}
|
||||
aria-label="Edit record"
|
||||
style={{
|
||||
background: "white",
|
||||
}}
|
||||
>
|
||||
<Pencil size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
<Tooltip label="Delete">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
|
||||
@@ -542,10 +542,14 @@ const RuleEngineResourcePage = () => {
|
||||
config={config}
|
||||
layout="row"
|
||||
readOnly={!canUpdateControls}
|
||||
onEdit={(record) => {
|
||||
setEditing(record);
|
||||
setFormOpen(true);
|
||||
}}
|
||||
onEdit={
|
||||
config.slug === "container-types"
|
||||
? undefined
|
||||
: (record) => {
|
||||
setEditing(record);
|
||||
setFormOpen(true);
|
||||
}
|
||||
}
|
||||
onDelete={setDeleteTarget}
|
||||
onViewChain={
|
||||
config.slug === "approval-rules"
|
||||
@@ -880,7 +884,9 @@ const RuleEngineResourcePage = () => {
|
||||
totalCount={totalCount}
|
||||
onPaginationChange={setPagination}
|
||||
readOnly={!canUpdate && !canDelete}
|
||||
onEdit={canUpdate ? openEdit : undefined}
|
||||
onEdit={
|
||||
canUpdate && config.slug !== "container-types" ? openEdit : undefined
|
||||
}
|
||||
onDelete={canDelete ? setDeleteTarget : undefined}
|
||||
onViewChain={
|
||||
config.slug === "approval-rules"
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
History,
|
||||
Inbox,
|
||||
PackageCheck,
|
||||
Plus,
|
||||
RefreshCw,
|
||||
Search,
|
||||
Send,
|
||||
@@ -311,19 +310,6 @@ export default function WagonTransfersPage() {
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
{canRequest ? (
|
||||
<Button
|
||||
color="edr-green"
|
||||
radius="md"
|
||||
leftSection={<Plus size={15} />}
|
||||
onClick={() => {
|
||||
setCarryOver(null);
|
||||
setFormOpen(true);
|
||||
}}
|
||||
>
|
||||
New request
|
||||
</Button>
|
||||
) : null}
|
||||
</Group>
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user