|
| 1 | +import { useGetUserInventory } from "@squonk/data-manager-client/inventory"; |
| 2 | + |
| 3 | +import dayjs from "dayjs"; |
| 4 | +import utc from "dayjs/plugin/utc"; |
| 5 | + |
| 6 | +dayjs.extend(utc); |
| 7 | + |
| 8 | +import { type UserDetail } from "@squonk/account-server-client"; |
| 9 | +import { useGetOrganisation } from "@squonk/account-server-client/organisation"; |
| 10 | +import { useGetUnits } from "@squonk/account-server-client/unit"; |
| 11 | +import { useGetOrganisationUsers } from "@squonk/account-server-client/user"; |
| 12 | +import { type InventoryUserDetail } from "@squonk/data-manager-client"; |
| 13 | + |
| 14 | +import { Alert, Container, Typography } from "@mui/material"; |
| 15 | +import { createColumnHelper } from "@tanstack/react-table"; |
| 16 | + |
| 17 | +import { CenterLoader } from "../CenterLoader"; |
| 18 | +import { DataTable } from "../DataTable"; |
| 19 | +import { NextLink } from "../NextLink"; |
| 20 | +import { getSharedColumns, type UserActivity } from "./sharedColumns"; |
| 21 | + |
| 22 | +type Unit = { |
| 23 | + id: string; |
| 24 | + name?: string; |
| 25 | +}; |
| 26 | + |
| 27 | +type InventoryWithUnit = UserActivity & { |
| 28 | + username: InventoryUserDetail["username"]; |
| 29 | + units: Unit[]; |
| 30 | +}; |
| 31 | + |
| 32 | +const columnHelper = createColumnHelper<InventoryWithUnit>(); |
| 33 | +const sharedColumns = getSharedColumns(columnHelper); |
| 34 | + |
| 35 | +export interface OrganisationUserUsageProps { |
| 36 | + organisationId: string; |
| 37 | +} |
| 38 | + |
| 39 | +export const OrganisationUserUsage = ({ organisationId }: OrganisationUserUsageProps) => { |
| 40 | + const { data: organisation } = useGetOrganisation(organisationId); |
| 41 | + const { data: units } = useGetUnits({ |
| 42 | + query: { select: (data) => data.units.flatMap((org) => org.units) }, |
| 43 | + }); |
| 44 | + const { data, error: inventoryError } = useGetUserInventory<InventoryWithUnit[]>( |
| 45 | + { org_id: organisationId }, |
| 46 | + { |
| 47 | + query: { |
| 48 | + select: (data) => { |
| 49 | + return data.users.map(({ projects, activity, first_seen, last_seen_date, username }) => ({ |
| 50 | + username, |
| 51 | + activity, |
| 52 | + first_seen, |
| 53 | + last_seen_date, |
| 54 | + units: Object.values(projects) |
| 55 | + .flat() |
| 56 | + .map(({ unit_id }) => ({ |
| 57 | + id: unit_id, |
| 58 | + name: units?.find((unit) => unit.id === unit_id)?.name, |
| 59 | + })) |
| 60 | + .reduce<Unit[]>((uniqueUnits, unit) => { |
| 61 | + // keep only unique units |
| 62 | + const existingUnit = uniqueUnits.find((u) => u.id === unit.id); |
| 63 | + if (!existingUnit) { |
| 64 | + uniqueUnits.push(unit); |
| 65 | + } |
| 66 | + return uniqueUnits; |
| 67 | + }, []), |
| 68 | + })); |
| 69 | + }, |
| 70 | + }, |
| 71 | + }, |
| 72 | + ); |
| 73 | + const { data: organisationMembers } = useGetOrganisationUsers(organisationId, { |
| 74 | + query: { |
| 75 | + enabled: organisation?.caller_is_member === undefined || organisation.caller_is_member, |
| 76 | + }, |
| 77 | + }); |
| 78 | + |
| 79 | + const columns = [ |
| 80 | + columnHelper.accessor("username", { header: "User" }), |
| 81 | + columnHelper.accessor("units", { |
| 82 | + header: "Units", |
| 83 | + cell: ({ getValue }) => { |
| 84 | + // keep only the unique units |
| 85 | + const units = getValue(); |
| 86 | + |
| 87 | + return ( |
| 88 | + <ul> |
| 89 | + {units.map((unit) => ( |
| 90 | + <li key={unit.id}> |
| 91 | + <NextLink |
| 92 | + component="a" |
| 93 | + href={{ pathname: "/unit/[unitId]/inventory", query: { unitId: unit.id } }} |
| 94 | + > |
| 95 | + {unit.name} |
| 96 | + </NextLink> |
| 97 | + </li> |
| 98 | + ))} |
| 99 | + </ul> |
| 100 | + ); |
| 101 | + }, |
| 102 | + }), |
| 103 | + ...sharedColumns, |
| 104 | + ]; |
| 105 | + |
| 106 | + if (inventoryError) { |
| 107 | + return <Alert severity="error">{inventoryError.message}</Alert>; |
| 108 | + } |
| 109 | + |
| 110 | + if (data === undefined) { |
| 111 | + return <CenterLoader />; |
| 112 | + } |
| 113 | + |
| 114 | + return ( |
| 115 | + <Container maxWidth="xl"> |
| 116 | + <Typography component="h2" variant="h1"> |
| 117 | + Organisation Inventory |
| 118 | + </Typography> |
| 119 | + <Typography variant="h3"> |
| 120 | + Organisation: <em>{organisation?.name}</em> |
| 121 | + </Typography> |
| 122 | + |
| 123 | + <Typography>Owner: {organisation?.owner_id}</Typography> |
| 124 | + <OrganisationMembers users={organisationMembers?.users} /> |
| 125 | + <DataTable columns={columns} data={data} /> |
| 126 | + </Container> |
| 127 | + ); |
| 128 | +}; |
| 129 | + |
| 130 | +const OrganisationMembers = ({ users }: { users: UserDetail[] | undefined }) => { |
| 131 | + // loading |
| 132 | + if (users === undefined) { |
| 133 | + return "Members: "; |
| 134 | + } |
| 135 | + |
| 136 | + // empty |
| 137 | + if (users.length === 0) { |
| 138 | + return ( |
| 139 | + <Typography> |
| 140 | + Members: <em>No members</em> |
| 141 | + </Typography> |
| 142 | + ); |
| 143 | + } |
| 144 | + |
| 145 | + return <Typography>Members: {users.map((user) => user.id).join(", ")}</Typography>; |
| 146 | +}; |
0 commit comments