feat(platform): checkpoint current implementation state
This commit is contained in:
@@ -26,6 +26,7 @@ const SkillMatrixUpload = dynamic(
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
import { ProgressRing } from "~/components/ui/ProgressRing.js";
|
||||
import { FadeIn } from "~/components/ui/FadeIn.js";
|
||||
import { CommentThread } from "~/components/comments/CommentThread.js";
|
||||
|
||||
interface ResourceDetailProps {
|
||||
resourceId: string;
|
||||
@@ -91,6 +92,7 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
const resource = _resourceQuery.data as unknown as Resource | undefined;
|
||||
const loadingResource = _resourceQuery.isLoading;
|
||||
const error = _resourceQuery.error;
|
||||
const errorCode = (error as any)?.data?.code as string | undefined;
|
||||
|
||||
// Fetch allocations for this resource (all non-cancelled)
|
||||
const now = new Date();
|
||||
@@ -119,6 +121,14 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
},
|
||||
{ enabled: !!resourceId },
|
||||
);
|
||||
const vacationList = (vacations ?? []) as Array<{
|
||||
endDate: Date | string;
|
||||
id: string;
|
||||
note?: string | null;
|
||||
startDate: Date | string;
|
||||
status: string;
|
||||
type: string;
|
||||
}>;
|
||||
|
||||
const chargeabilityStatsResult = trpc.resource.getChargeabilityStats.useQuery(
|
||||
{ includeProposed: includeProposedChargeability, resourceId },
|
||||
@@ -143,7 +153,7 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !resource) {
|
||||
if (errorCode === "NOT_FOUND") {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="bg-red-50 border border-red-200 rounded-xl p-6 text-red-700 text-sm">
|
||||
@@ -154,6 +164,17 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !resource) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-xl p-6 text-amber-800 text-sm">
|
||||
This resource could not be loaded right now.{" "}
|
||||
<Link href="/resources" className="underline">Back to resources</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const skills = resource.skills as unknown as SkillEntry[];
|
||||
const resourceRoles = (resource as unknown as {
|
||||
resourceRoles?: { isPrimary: boolean; role: { id: string; name: string; color: string | null } }[];
|
||||
@@ -433,6 +454,24 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
onGenerated={async () => { await utils.resource.getById.invalidate({ id: resourceId }); }}
|
||||
/>
|
||||
|
||||
<section
|
||||
id="comments"
|
||||
className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-5 scroll-mt-24"
|
||||
>
|
||||
<div className="mb-4">
|
||||
<h2 className="text-sm font-semibold text-gray-800 dark:text-gray-200">Comments</h2>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Discussion for this resource follows the same visibility as the resource detail itself.
|
||||
</p>
|
||||
</div>
|
||||
<CommentThread
|
||||
commentTarget={{
|
||||
entityType: "resource",
|
||||
entityId: resourceId,
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Main Skills Badges */}
|
||||
{mainSkills.length > 0 && (
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-5">
|
||||
@@ -594,11 +633,11 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
</div>
|
||||
{loadingVacations ? (
|
||||
<div className="p-6 text-center text-gray-400 text-sm animate-pulse">Loading…</div>
|
||||
) : (vacations ?? []).length === 0 ? (
|
||||
) : vacationList.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-400 text-sm">No vacations recorded.</div>
|
||||
) : (
|
||||
<div className="divide-y divide-gray-100">
|
||||
{(vacations ?? []).map((v) => {
|
||||
{vacationList.map((v) => {
|
||||
const days =
|
||||
Math.round(
|
||||
(new Date(v.endDate).getTime() - new Date(v.startDate).getTime()) / (1000 * 60 * 60 * 24),
|
||||
|
||||
Reference in New Issue
Block a user