@mintplayer/ng-spark 22.26.0 → 22.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/fesm2022/mintplayer-ng-spark-grid.mjs +73 -50
- package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-history.mjs +1 -1
- package/fesm2022/mintplayer-ng-spark-history.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-models.mjs +19 -10
- package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-moderation.mjs +1 -1
- package/fesm2022/mintplayer-ng-spark-moderation.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-pipes.mjs +5 -2
- package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-create.mjs +16 -7
- package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +30 -14
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs +45 -15
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +18 -2
- package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-renderers.mjs +2 -1
- package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +0 -1
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +12 -9
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +4 -3
- package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -1
- package/package.json +3 -3
- package/types/mintplayer-ng-spark-grid.d.ts +50 -46
- package/types/mintplayer-ng-spark-models.d.ts +75 -40
- package/types/mintplayer-ng-spark-moderation.d.ts +8 -4
- package/types/mintplayer-ng-spark-pipes.d.ts +3 -0
- package/types/mintplayer-ng-spark-po-create.d.ts +4 -0
- package/types/mintplayer-ng-spark-po-detail.d.ts +12 -2
- package/types/mintplayer-ng-spark-po-edit.d.ts +16 -0
- package/types/mintplayer-ng-spark-po-form.d.ts +1 -0
- package/types/mintplayer-ng-spark-query-list.d.ts +11 -1
- package/types/mintplayer-ng-spark-services.d.ts +38 -9
- package/types/mintplayer-ng-spark-soft-delete.d.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark-moderation.mjs","names":[],"sources":["../../moderation/src/spark-moderation.service.ts","../../moderation/src/spark-moderation.tokens.ts","../../moderation/src/spark-vote.component.ts","../../moderation/src/spark-flag-button.component.ts","../../moderation/src/spark-moderator-panel.component.ts","../../moderation/src/spark-reputation-badge.component.ts","../../moderation/src/spark-review-queue.component.ts","../../moderation/src/spark-review-queue-link.component.ts","../../moderation/src/provide-spark-moderation.ts"],"sourcesContent":["import { computed, inject, Injectable, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { EMPTY, filter } from 'rxjs';\nimport { SparkService } from '@mintplayer/ng-spark/services';\nimport {\n ModerationAuditEntry,\n ModerationCaseDetail,\n ModerationCaseSummary,\n ModerationReputation,\n ModerationReputationLine,\n ModerationStatus,\n ModerationVoteState,\n} from './spark-moderation.models';\n\n/**\n * Client for `MintPlayer.Spark.Moderation`'s endpoints (`/spark/moderation/*`, #460). Every call is\n * a POST with the ids in the body — the route table is literal and RavenDB ids contain slashes.\n *\n * A target the caller cannot see (hidden, missing, not moderatable) is the standard refusal: 404\n * (or 401 when signing in would help). A rule (own post, locked, suspended) is a 400 with `errors`,\n * a quota (votes per day) a 429 whose `result.retryAfterSeconds` says when to try again.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkModerationService {\n private readonly spark = inject(SparkService);\n\n // Vote states are fetched in batches: every widget that asks within one macrotask shares a request.\n private readonly pending = new Map<string, Map<string, ((state: ModerationVoteState | null) => void)[]>>();\n private flushScheduled = false;\n\n private readonly router = inject(Router, { optional: true });\n\n readonly #votesCast = signal(0);\n\n /** Counts the caller's accepted votes, so the own reputation badge re-reads (a downvote costs reputation). */\n readonly votesCast = this.#votesCast.asReadonly();\n\n private readonly navigated = toSignal(\n this.router?.events.pipe(filter(e => e instanceof NavigationEnd)) ?? EMPTY,\n { initialValue: null });\n\n /**\n * Changes whenever the caller's own reputation may have changed: after every navigation (a\n * sign-in, a sign-out, another page) and after the caller's own votes. The own badge and the\n * review-queue link read it to re-fetch {@link ownReputation}; a component in the app shell lives\n * for the whole session and would otherwise show the first answer until a full reload.\n */\n // A fresh object each time: returning the vote count alone would compare equal after a navigation\n // and notify nobody.\n readonly ownReputationChanged = computed(() => ({ navigation: this.navigated(), votes: this.#votesCast() }));\n\n #ownInFlight: Promise<ModerationReputation> | null = null;\n\n /**\n * The caller's own reputation, shared by every caller in flight at once: the badge and the\n * review-queue link both re-read on the same navigation and cost one request.\n */\n ownReputation(): Promise<ModerationReputation> {\n return this.#ownInFlight ??= this.reputation().finally(() => this.#ownInFlight = null);\n }\n\n /** Casts (+1 / −1) or withdraws (0) the caller's vote; resolves to the target's new state. */\n async vote(type: string, id: string, direction: -1 | 0 | 1): Promise<ModerationVoteState> {\n const state = await this.spark.postEnvelope<ModerationVoteState>('/moderation/vote', { objectTypeId: type, id, direction });\n this.#votesCast.update(n => n + 1);\n return state;\n }\n\n /**\n * The score and the caller's vote for one target, batched with every other call made in the same\n * macrotask for the same type (one `POST /spark/moderation/votes`, ≤ 100 ids). Resolves to `null`\n * for an id the server left out (not visible to the caller).\n */\n voteState(type: string, id: string): Promise<ModerationVoteState | null> {\n return new Promise(resolve => {\n let byId = this.pending.get(type);\n if (!byId) this.pending.set(type, (byId = new Map()));\n const waiting = byId.get(id) ?? [];\n waiting.push(resolve);\n byId.set(id, waiting);\n if (!this.flushScheduled) {\n this.flushScheduled = true;\n setTimeout(() => this.flush(), 0);\n }\n });\n }\n\n /** `POST /spark/moderation/votes` for up to 100 ids of one type. */\n votes(type: string, ids: string[]): Promise<ModerationVoteState[]> {\n return this.spark.postEnvelope<ModerationVoteState[]>('/moderation/votes', { objectTypeId: type, ids });\n }\n\n flag(type: string, id: string, reason: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/flag', { objectTypeId: type, id, reason });\n }\n\n lock(type: string, id: string, reason?: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/lock', { objectTypeId: type, id, reason });\n }\n\n unlock(type: string, id: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/unlock', { objectTypeId: type, id });\n }\n\n /** What the moderator panel shows about a target, including which tools the caller holds. */\n status(type: string, id: string): Promise<ModerationStatus> {\n return this.spark.postEnvelope<ModerationStatus>('/moderation/status', { objectTypeId: type, id });\n }\n\n /** A user's reputation; the caller's own when `userId` is omitted (with privileges and suspension). */\n reputation(userId?: string): Promise<ModerationReputation> {\n return this.spark.postEnvelope<ModerationReputation>('/moderation/reputation', userId ? { userId } : {});\n }\n\n /** The caller's own ledger, newest first. Voters are never named; a reversal reads \"Voting corrected (−N)\". */\n history(take = 100): Promise<ModerationReputationLine[]> {\n return this.spark.postEnvelope<ModerationReputationLine[]>('/moderation/reputation/history', { take });\n }\n\n cases(status: string = 'open', skip = 0, take = 50): Promise<ModerationCaseSummary[]> {\n return this.spark.postEnvelope<ModerationCaseSummary[]>('/moderation/cases', { status, skip, take });\n }\n\n case(caseId: string): Promise<ModerationCaseDetail> {\n return this.spark.postEnvelope<ModerationCaseDetail>('/moderation/case', { caseId });\n }\n\n /** `uphold` / `decline` (flag cases), `dismiss` / `reverse` / `merge` / `suspend` (fraud cases; the last two name `accountId`). */\n decide(caseId: string, decision: string, accountId?: string, reason?: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/case/decide', { caseId, decision, accountId, reason });\n }\n\n suspend(userId: string, days?: number | null, reason?: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/suspend', { userId, days: days ?? null, reason });\n }\n\n unsuspend(userId: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/unsuspend', { userId });\n }\n\n audit(skip = 0, take = 50): Promise<ModerationAuditEntry[]> {\n return this.spark.postEnvelope<ModerationAuditEntry[]>('/moderation/audit', { skip, take });\n }\n\n private flush(): void {\n this.flushScheduled = false;\n const batches = [...this.pending.entries()];\n this.pending.clear();\n for (const [type, byId] of batches) {\n const ids = [...byId.keys()];\n for (let i = 0; i < ids.length; i += 100) {\n const chunk = ids.slice(i, i + 100);\n this.votes(type, chunk).then(\n states => {\n const found = new Map(states.map(s => [s.id, s] as const));\n for (const id of chunk) byId.get(id)?.forEach(resolve => resolve(found.get(id) ?? null));\n },\n () => {\n for (const id of chunk) byId.get(id)?.forEach(resolve => resolve(null));\n });\n }\n }\n }\n}\n","import { InjectionToken } from '@angular/core';\n\n/** Where the review queue is routed; the moderator panel links there. Set by {@link provideSparkModeration}. */\nexport const SPARK_MODERATION_REVIEW_PATH = new InjectionToken<string>('SparkModerationReviewPath', {\n factory: () => '/moderation/review',\n});\n","import { ChangeDetectionStrategy, Component, computed, effect, inject, input, signal } from '@angular/core';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { EntityAttributeDefinition, PersistentObject, QueryResultItem, SparkCellColumn } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationVoteState } from './spark-moderation.models';\n\n/**\n * The vote widget (#460): up, score, down. An attribute renderer — register it with\n * {@link sparkModerationRenderers} and give an attribute of an `IModeratable` type\n * `\"renderer\": \"spark-vote\"`. The attribute's own value is ignored: the score comes from\n * `/spark/moderation/votes` (batched per page), never from the document, so a vote never moves the\n * row's etag.\n *\n * An arrow the caller holds no right for (`canUpvote` / `canDownvote` false on the state) is\n * disabled, so a user without the privilege sees the score but is not offered a click that the server\n * refuses — except the active arrow, since withdrawing a vote needs no right.\n *\n * On the detail page the entity type is the object's. In a query grid a row carries no type, so the\n * column needs `\"rendererOptions\": { \"type\": \"<entity type id or alias>\" }`.\n */\n@Component({\n selector: 'spark-vote',\n imports: [TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (state(); as s) {\n <span class=\"spark-vote d-inline-flex align-items-center gap-1\" [class.spark-vote-locked]=\"s.locked\">\n <button type=\"button\" class=\"btn btn-sm spark-vote-up\" [class.btn-primary]=\"s.myVote === 1\" [class.btn-outline-secondary]=\"s.myVote !== 1\"\n [disabled]=\"busy() || s.locked || (s.canUpvote === false && s.myVote !== 1)\" [attr.aria-pressed]=\"s.myVote === 1\" [attr.aria-label]=\"'moderation.upvote' | t\" (click)=\"cast(1)\">▲</button>\n <span class=\"spark-vote-score fw-bold\" [attr.aria-label]=\"'moderation.score' | t\">{{ s.score }}</span>\n <button type=\"button\" class=\"btn btn-sm spark-vote-down\" [class.btn-danger]=\"s.myVote === -1\" [class.btn-outline-secondary]=\"s.myVote !== -1\"\n [disabled]=\"busy() || s.locked || (s.canDownvote === false && s.myVote !== -1)\" [attr.aria-pressed]=\"s.myVote === -1\" [attr.aria-label]=\"'moderation.downvote' | t\" (click)=\"cast(-1)\">▼</button>\n @if (error(); as message) {\n <span class=\"text-danger small spark-vote-error\" role=\"alert\">{{ message }}</span>\n }\n </span>\n }\n `,\n})\nexport class SparkVoteComponent {\n private readonly moderation = inject(SparkModerationService);\n\n // Renderer inputs (all optional: the hosts pass only what a renderer declares).\n value = input<unknown>();\n attribute = input<EntityAttributeDefinition | undefined>();\n column = input<SparkCellColumn | undefined>();\n options = input<Record<string, any> | undefined>();\n item = input<PersistentObject | QueryResultItem | Record<string, any> | undefined>();\n\n protected readonly state = signal<ModerationVoteState | null>(null);\n protected readonly busy = signal(false);\n protected readonly error = signal<string | null>(null);\n\n protected readonly target = computed(() => {\n const item = this.item() as Record<string, any> | undefined;\n const id = item?.['id'] as string | undefined;\n const type = (item?.['objectTypeId'] as string | undefined) ?? (this.options()?.['type'] as string | undefined);\n return id && type ? { id, type } : null;\n });\n\n constructor() {\n effect(() => {\n const target = this.target();\n if (!target) return;\n this.moderation.voteState(target.type, target.id).then(s => this.state.set(s));\n });\n }\n\n async cast(direction: 1 | -1): Promise<void> {\n const target = this.target();\n const current = this.state();\n if (!target || !current || this.busy()) return;\n // Clicking the active arrow withdraws the vote.\n const next = current.myVote === direction ? 0 : direction;\n this.busy.set(true);\n this.error.set(null);\n try {\n this.state.set(await this.moderation.vote(target.type, target.id, next));\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n\n/** A readable message for a refused moderation call (400 errors, 429 quota, 404 refusal). */\nexport function describeModerationError(e: unknown): string {\n const err = e as HttpErrorResponse;\n const body = err?.error as { result?: { errors?: { errorMessage?: unknown }[]; error?: string } } | undefined;\n const first = body?.result?.errors?.[0]?.errorMessage;\n if (typeof first === 'string') return first;\n if (first && typeof first === 'object') {\n const values = Object.values(first as Record<string, string>);\n if (values.length) return values[0];\n }\n return body?.result?.error ?? err?.message ?? 'Failed';\n}\n","import { ChangeDetectionStrategy, Component, inject, input, signal } from '@angular/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { SparkDetailContext } from '@mintplayer/ng-spark/panels';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { describeModerationError } from './spark-vote.component';\n\n/**\n * **Flag** on the detail page of every object (#460). Registered by {@link provideSparkModeration}\n * as a `SPARK_DETAIL_ACTIONS` entry. The button is offered to every signed-in viewer of a live row;\n * the server decides (`Flag/T`, a visible moderatable row) and answers a refusal inline — the\n * permissions endpoint does not report moderation rights, and a type that is not moderatable answers\n * like a missing row, so the button cannot know in advance.\n */\n@Component({\n selector: 'spark-flag-button',\n imports: [TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (context().deleted !== 'only') {\n <span class=\"d-inline-flex gap-2 align-items-center\">\n <button type=\"button\" class=\"btn btn-outline-warning spark-flag\" [disabled]=\"busy() || done()\" (click)=\"flag()\">\n {{ 'moderation.flag' | t }}\n </button>\n @if (done()) {\n <span class=\"small text-success spark-flag-done\">{{ 'moderation.flagged' | t }}</span>\n }\n @if (error(); as message) {\n <span class=\"small text-danger spark-flag-error\" role=\"alert\">{{ message }}</span>\n }\n </span>\n }\n `,\n})\nexport class SparkFlagButtonComponent {\n private readonly moderation = inject(SparkModerationService);\n private readonly lang = inject(SparkLanguageService);\n\n context = input.required<SparkDetailContext>();\n\n protected readonly busy = signal(false);\n protected readonly done = signal(false);\n protected readonly error = signal<string | null>(null);\n\n async flag(): Promise<void> {\n const reason = prompt(this.lang.t('moderation.flagReason'));\n if (!reason?.trim()) return;\n const ctx = this.context();\n this.busy.set(true);\n this.error.set(null);\n try {\n await this.moderation.flag(ctx.type, ctx.id, reason.trim());\n this.done.set(true);\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, effect, inject, input, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { SparkDetailContext } from '@mintplayer/ng-spark/panels';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationStatus } from './spark-moderation.models';\nimport { describeModerationError } from './spark-vote.component';\nimport { SPARK_MODERATION_REVIEW_PATH } from './spark-moderation.tokens';\n\n/**\n * The moderator panel under a moderatable object's detail page (#460): lock state and Lock/Unlock\n * (`Lock/T`), open flags with a link to the review queue (`Review/Moderation`), and Suspend author\n * (`Suspend/Moderation`). Restore/Purge and Revert are the SoftDelete and History entry points'.\n * Renders nothing for a caller with none of these rights, or for a type that is not moderatable.\n */\n@Component({\n selector: 'spark-moderator-panel',\n imports: [BsCardComponent, BsCardHeaderComponent, TranslateKeyPipe, RouterLink],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (status(); as s) {\n @if (s.canLock || s.canReview || s.canSuspend) {\n <bs-card class=\"d-block spark-moderator-panel\">\n <bs-card-header>{{ 'moderation.panel' | t }}</bs-card-header>\n <div class=\"p-3 d-flex flex-wrap gap-2 align-items-center\">\n @if (s.locked) {\n <span class=\"badge bg-secondary spark-moderation-locked\">{{ 'moderation.locked' | t }}</span>\n @if (s.lockReason) { <span class=\"small text-muted\">{{ s.lockReason }}</span> }\n }\n @if (s.canLock) {\n @if (s.locked) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-unlock\" [disabled]=\"busy()\" (click)=\"unlock()\">{{ 'moderation.unlock' | t }}</button>\n } @else {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-lock\" [disabled]=\"busy()\" (click)=\"lock()\">{{ 'moderation.lock' | t }}</button>\n }\n }\n @if (s.canReview && s.openFlags > 0) {\n <a class=\"btn btn-sm btn-outline-warning spark-open-flags\" [routerLink]=\"reviewPath\" [queryParams]=\"{ case: s.openCaseId }\">\n {{ 'moderation.openFlags' | t }}: {{ s.openFlags }}\n </a>\n }\n @if (s.canSuspend && s.authorId) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger spark-suspend-author\" [disabled]=\"busy()\" (click)=\"suspendAuthor(s.authorId)\">{{ 'moderation.suspendAuthor' | t }}</button>\n }\n @if (error(); as message) {\n <span class=\"small text-danger\" role=\"alert\">{{ message }}</span>\n }\n </div>\n </bs-card>\n }\n }\n `,\n})\nexport class SparkModeratorPanelComponent {\n private readonly moderation = inject(SparkModerationService);\n private readonly lang = inject(SparkLanguageService);\n protected readonly reviewPath = inject(SPARK_MODERATION_REVIEW_PATH);\n\n context = input.required<SparkDetailContext>();\n\n protected readonly status = signal<ModerationStatus | null>(null);\n protected readonly busy = signal(false);\n protected readonly error = signal<string | null>(null);\n\n constructor() {\n effect(() => {\n const ctx = this.context();\n if (ctx.deleted === 'only') {\n this.status.set(null);\n return;\n }\n // A refusal (not moderatable, not visible) simply hides the panel.\n this.moderation.status(ctx.type, ctx.id).then(s => this.status.set(s), () => this.status.set(null));\n });\n }\n\n async lock(): Promise<void> {\n const reason = prompt(this.lang.t('moderation.lockReason')) ?? undefined;\n await this.run(() => this.moderation.lock(this.context().type, this.context().id, reason));\n }\n\n async unlock(): Promise<void> {\n await this.run(() => this.moderation.unlock(this.context().type, this.context().id));\n }\n\n async suspendAuthor(userId: string): Promise<void> {\n const answer = prompt(this.lang.t('moderation.suspendDays'));\n if (answer === null) return;\n const days = answer.trim() === '' ? null : Number(answer);\n if (days !== null && !(days > 0)) return;\n await this.run(() => this.moderation.suspend(userId, days));\n }\n\n private async run(work: () => Promise<void>): Promise<void> {\n this.busy.set(true);\n this.error.set(null);\n try {\n await work();\n const ctx = this.context();\n this.status.set(await this.moderation.status(ctx.type, ctx.id));\n await ctx.reload();\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, effect, inject, input, signal } from '@angular/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationReputation } from './spark-moderation.models';\n\n/**\n * A user's reputation (#460): `<spark-reputation-badge [userId]=\"post.authorId\" />`, or without\n * `userId` the signed-in user's own, with the not-yet-credited part (delayed crediting) beside it.\n *\n * The own badge usually sits in the app shell, which lives for the whole session: it re-reads the\n * reputation after every navigation (a sign-in, a sign-out, opening another page) and after the\n * caller's own votes, or it would show the first value it read until a full page reload.\n */\n@Component({\n selector: 'spark-reputation-badge',\n imports: [TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (reputation(); as r) {\n <span class=\"badge bg-body-tertiary text-body border spark-reputation-badge\" [attr.title]=\"'moderation.reputation' | t\">\n {{ r.total }}\n @if (!userId() && r.pending) {\n <!-- A received downvote makes the pending part negative: \"(-2 pending)\", never \"(+-2 pending)\". -->\n <span class=\"text-muted spark-reputation-pending\">({{ r.pending > 0 ? '+' : '' }}{{ r.pending }} {{ 'moderation.pending' | t }})</span>\n }\n </span>\n }\n `,\n})\nexport class SparkReputationBadgeComponent {\n private readonly moderation = inject(SparkModerationService);\n\n userId = input<string | null | undefined>();\n\n protected readonly reputation = signal<ModerationReputation | null>(null);\n\n constructor() {\n effect(() => {\n const userId = this.userId() ?? undefined;\n // The own badge only: someone else's total does not move because the viewer navigated.\n const read = userId\n ? this.moderation.reputation(userId)\n : (this.moderation.ownReputationChanged(), this.moderation.ownReputation());\n read.then(r => this.reputation.set(r), () => this.reputation.set(null));\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { DatePipe } from '@angular/common';\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { EntityType } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationCaseDetail, ModerationCaseSummary } from './spark-moderation.models';\nimport { describeModerationError } from './spark-vote.component';\n\n/**\n * The review queue (#460): open cases on the left, the selected case's review surface on the right —\n * the voter→target matrix, the timeline against the posts' creation, the accounts side by side (age,\n * registration method, how many accounts share a network with it, reputation now and after reversing)\n * and the rule that fired — with the decisions the case allows. Needs `Review/Moderation`; merge and\n * suspend also `Suspend/Moderation` (the server refuses otherwise).\n *\n * Routed by {@link sparkModerationRoutes} at `moderation/review`; `?case=` selects a case.\n */\n@Component({\n selector: 'spark-review-queue',\n imports: [BsCardComponent, BsCardHeaderComponent, RouterLink, TranslateKeyPipe, DatePipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"container-fluid spark-review-queue\">\n <h1 class=\"h3\">{{ 'moderation.reviewQueue' | t }}</h1>\n @if (error(); as message) { <div class=\"alert alert-danger\" role=\"alert\">{{ message }}</div> }\n <div class=\"row g-3\">\n <div class=\"col-md-4\">\n @for (c of cases(); track c.id) {\n <button type=\"button\" class=\"list-group-item list-group-item-action w-100 text-start p-2 border mb-1 spark-case\"\n [class.active]=\"selected()?.case?.id === c.id\" (click)=\"open(c.id)\">\n <strong>{{ c.kind }}</strong>\n @if (c.flagCount) { <span class=\"badge text-bg-warning ms-1\">{{ c.flagCount }}</span> }\n @if (targets()[c.id]?.title; as title) { <div class=\"small fw-semibold text-truncate spark-case-target\">{{ title }}</div> }\n <div class=\"small\">{{ c.summary }}</div>\n <div class=\"small text-muted\">{{ c.openedAtUtc | date: 'short' }}</div>\n </button>\n } @empty {\n <p class=\"text-muted spark-no-cases\">{{ 'moderation.noCases' | t }}</p>\n }\n </div>\n <div class=\"col-md-8\">\n @if (selected(); as d) {\n <bs-card class=\"d-block spark-case-detail\">\n <bs-card-header>{{ 'moderation.rule' | t }}: {{ d.case.kind }} — {{ d.case.summary }}</bs-card-header>\n <div class=\"p-3\">\n @if (targets()[d.case.id]; as target) {\n <p class=\"spark-case-target-link\">\n @if (target.link) { <a [routerLink]=\"target.link\">{{ target.title ?? d.case.targetId }}</a> }\n @else { {{ target.title ?? d.case.targetId }} }\n </p>\n }\n @if (d.accounts.length) {\n <h2 class=\"h6\">{{ 'moderation.accounts' | t }}</h2>\n <table class=\"table table-sm spark-case-accounts\">\n <thead><tr><th></th><th>{{ 'moderation.ageDays' | t }}</th><th></th><th>{{ 'moderation.sharesNetwork' | t }}</th><th>{{ 'moderation.repBefore' | t }}</th><th>{{ 'moderation.repAfter' | t }}</th></tr></thead>\n <tbody>\n @for (a of d.accounts; track a.id) {\n <tr><td>{{ a.id }}</td><td>{{ a.ageDays }}</td><td>{{ a.registrationMethod }}</td><td>{{ a.sharesNetworkWith }}</td><td>{{ a.reputationBefore }}</td><td>{{ a.reputationAfter }}</td></tr>\n }\n </tbody>\n </table>\n }\n @if (d.matrix.length) {\n <h2 class=\"h6\">{{ 'moderation.matrix' | t }}</h2>\n <ul class=\"small spark-case-matrix\">\n @for (m of d.matrix; track m.voterId + m.authorId) { <li>{{ m.voterId }} → {{ m.authorId }}: {{ m.votes }}</li> }\n </ul>\n }\n @if (d.timeline.length) {\n <h2 class=\"h6\">{{ 'moderation.timeline' | t }}</h2>\n <table class=\"table table-sm small spark-case-timeline\">\n <tbody>\n @for (t of d.timeline; track t.voteId) {\n <tr><td>{{ t.castAtUtc | date: 'short' }}</td><td>{{ t.voterId }} → {{ t.authorId }}</td><td>{{ t.direction > 0 ? '▲' : t.direction < 0 ? '▼' : '·' }}</td><td>{{ t.secondsAfterPost ?? '' }} s</td></tr>\n }\n </tbody>\n </table>\n }\n @if (d.flags.length) {\n <ul class=\"small spark-case-flags\">\n @for (f of d.flags; track f.flaggerId) { <li>{{ f.reason }} <span class=\"text-muted\">({{ f.raisedAtUtc | date: 'short' }})</span></li> }\n </ul>\n }\n @if (d.case.status === 'open') {\n <div class=\"d-flex flex-wrap gap-2\">\n @if (d.case.kind === 'flag') {\n <button type=\"button\" class=\"btn btn-sm btn-success spark-decide-uphold\" [disabled]=\"busy()\" (click)=\"decide('uphold')\">{{ 'moderation.uphold' | t }}</button>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-decide-decline\" [disabled]=\"busy()\" (click)=\"decide('decline')\">{{ 'moderation.decline' | t }}</button>\n } @else {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-decide-dismiss\" [disabled]=\"busy()\" (click)=\"decide('dismiss')\">{{ 'moderation.dismiss' | t }}</button>\n <button type=\"button\" class=\"btn btn-sm btn-warning spark-decide-reverse\" [disabled]=\"busy()\" (click)=\"decide('reverse')\">{{ 'moderation.reverse' | t }}</button>\n @for (a of d.case.accountIds; track a) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger spark-decide-merge\" [disabled]=\"busy()\" (click)=\"decide('merge', a)\">{{ 'moderation.merge' | t }}: {{ a }}</button>\n <button type=\"button\" class=\"btn btn-sm btn-danger spark-decide-suspend\" [disabled]=\"busy()\" (click)=\"decide('suspend', a)\">{{ 'moderation.suspend' | t }}: {{ a }}</button>\n }\n }\n </div>\n }\n </div>\n </bs-card>\n }\n </div>\n </div>\n </div>\n `,\n})\nexport class SparkReviewQueueComponent implements OnInit {\n private readonly moderation = inject(SparkModerationService);\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n\n private readonly spark = inject(SparkService);\n\n protected readonly cases = signal<ModerationCaseSummary[]>([]);\n /** Per case id: the target post's title and route, once resolved. */\n protected readonly targets = signal<Record<string, { title: string | null; link: string[] | null }>>({});\n protected readonly selected = signal<ModerationCaseDetail | null>(null);\n protected readonly busy = signal(false);\n protected readonly error = signal<string | null>(null);\n\n async ngOnInit(): Promise<void> {\n await this.load();\n const requested = this.route.snapshot.queryParamMap.get('case');\n if (requested) await this.open(requested);\n }\n\n async open(caseId: string): Promise<void> {\n await this.run(async () => {\n this.selected.set(await this.moderation.case(caseId));\n await this.router.navigate([], { relativeTo: this.route, queryParams: { case: caseId }, replaceUrl: true });\n });\n }\n\n async decide(decision: string, accountId?: string): Promise<void> {\n const current = this.selected();\n if (!current) return;\n await this.run(async () => {\n await this.moderation.decide(current.case.id, decision, accountId);\n this.selected.set(await this.moderation.case(current.case.id));\n await this.load();\n });\n }\n\n private async load(): Promise<void> {\n await this.run(async () => this.cases.set(await this.moderation.cases('open')));\n void this.resolveTargets(this.cases());\n }\n\n /**\n * Names each case's post: its title (the object's breadcrumb) and a link to it. A case carries only\n * the target's id and type name; without this the queue listed \"flag · Spam\" with no way to tell\n * which post it was about. A target the caller cannot open (purged, hidden) keeps just its id.\n */\n private async resolveTargets(cases: ModerationCaseSummary[]): Promise<void> {\n const pending = cases.filter(c => c.targetId && c.targetType && !(c.id in this.targets()));\n if (!pending.length) return;\n let types: EntityType[] = [];\n try {\n types = await this.spark.getEntityTypes();\n } catch {\n return;\n }\n await Promise.all(pending.map(async c => {\n const type = types.find(t => t.name === c.targetType || t.id === c.targetType || t.alias === c.targetType);\n const route = type ? type.alias || type.id : null;\n let title: string | null = null;\n if (route) {\n try {\n const po = await this.spark.get(route, c.targetId!);\n title = po.breadcrumb || null;\n } catch {\n // Not visible to the caller (or gone): the id stays the label, without a link.\n }\n }\n const link = route && title ? ['/po', route, c.targetId!] : null;\n this.targets.update(t => ({ ...t, [c.id]: { title, link } }));\n }));\n }\n\n private async run(work: () => Promise<void>): Promise<void> {\n this.busy.set(true);\n this.error.set(null);\n try {\n await work();\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, effect, inject, input, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkModerationService } from './spark-moderation.service';\n\n/**\n * A link to the review queue (#460) that renders only for a caller who may review — earned, or\n * granted by a group such as moderators (`canReview` on the caller's own reputation). Anyone else\n * would open a page whose every request answers 404. Re-checked after each navigation, so it appears\n * on sign-in and disappears on sign-out.\n *\n * `<spark-review-queue-link linkClass=\"btn btn-sm btn-link\" />` — `linkClass` styles the anchor.\n */\n@Component({\n selector: 'spark-review-queue-link',\n imports: [RouterLink, TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[style.display]': 'canReview() ? null : \"none\"' },\n template: `\n @if (canReview()) {\n <a class=\"spark-review-queue-link\" [class]=\"linkClass()\" routerLink=\"/moderation/review\">{{ 'moderation.reviewQueue' | t }}</a>\n }\n `,\n})\nexport class SparkReviewQueueLinkComponent {\n private readonly moderation = inject(SparkModerationService);\n\n /** Classes for the anchor itself (the host element is only a wrapper). */\n linkClass = input<string>('');\n\n protected readonly canReview = signal(false);\n\n constructor() {\n effect(() => {\n this.moderation.ownReputationChanged();\n this.moderation.ownReputation().then(r => this.canReview.set(r.canReview === true), () => this.canReview.set(false));\n });\n }\n}\n","import { Provider } from '@angular/core';\nimport { Routes } from '@angular/router';\nimport { SparkAttributeRendererRegistration } from '@mintplayer/ng-spark/renderers';\nimport { provideSparkDetailActions, provideSparkDetailPanels } from '@mintplayer/ng-spark/panels';\nimport { SparkFlagButtonComponent } from './spark-flag-button.component';\nimport { SparkModeratorPanelComponent } from './spark-moderator-panel.component';\nimport { SparkReviewQueueComponent } from './spark-review-queue.component';\nimport { SparkVoteComponent } from './spark-vote.component';\nimport { SPARK_MODERATION_REVIEW_PATH } from './spark-moderation.tokens';\n\n/**\n * Adds the Moderation UI (#460) to the pages `sparkRoutes()` routes to: a **Flag** action on every\n * detail page and the **moderator panel** under it. Needs the server package\n * `MintPlayer.Spark.Moderation`.\n *\n * ```ts\n * providers: [\n * provideSpark(...),\n * provideSparkModeration(),\n * provideSparkAttributeRenderers([...sparkModerationRenderers, ...yourRenderers]),\n * ]\n * // routes: [...sparkModerationRoutes(), ...sparkRoutes()]\n * ```\n *\n * The vote widget is an attribute renderer (`\"renderer\": \"spark-vote\"` on an attribute of an\n * `IModeratable` type), so it is registered through {@link sparkModerationRenderers} with the app's\n * own renderers — `provideSparkAttributeRenderers` takes one list.\n */\nexport function provideSparkModeration(options?: { reviewPath?: string; panelOrder?: number }): Provider[] {\n return [\n ...provideSparkDetailActions({ id: 'spark-moderation-flag', component: SparkFlagButtonComponent, priority: 90 }),\n ...provideSparkDetailPanels({ id: 'spark-moderation-panel', component: SparkModeratorPanelComponent, order: options?.panelOrder ?? 90 }),\n { provide: SPARK_MODERATION_REVIEW_PATH, useValue: options?.reviewPath ?? '/moderation/review' },\n ];\n}\n\n/** The vote widget as an attribute renderer named `spark-vote` (detail page and grid column). */\nexport const sparkModerationRenderers: SparkAttributeRendererRegistration[] = [\n { name: 'spark-vote', detailComponent: SparkVoteComponent, columnComponent: SparkVoteComponent },\n];\n\n/**\n * The review-queue page at `moderation/review`. Put it **before** `sparkRoutes()` — a literal first\n * segment is fine anywhere, but keeping add-on routes first avoids surprises with parameterised ones.\n */\nexport function sparkModerationRoutes(path = 'moderation/review'): Routes {\n return [{ path, component: SparkReviewQueueComponent }];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-moderation.mjs","names":[],"sources":["../../moderation/src/spark-moderation.service.ts","../../moderation/src/spark-moderation.tokens.ts","../../moderation/src/spark-vote.component.ts","../../moderation/src/spark-flag-button.component.ts","../../moderation/src/spark-moderator-panel.component.ts","../../moderation/src/spark-reputation-badge.component.ts","../../moderation/src/spark-review-queue.component.ts","../../moderation/src/spark-review-queue-link.component.ts","../../moderation/src/provide-spark-moderation.ts"],"sourcesContent":["import { computed, inject, Injectable, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { EMPTY, filter } from 'rxjs';\nimport { SparkService } from '@mintplayer/ng-spark/services';\nimport {\n ModerationAuditEntry,\n ModerationCaseDetail,\n ModerationCaseSummary,\n ModerationReputation,\n ModerationReputationLine,\n ModerationStatus,\n ModerationVoteState,\n} from './spark-moderation.models';\n\n/**\n * Client for `MintPlayer.Spark.Moderation`'s endpoints (`/spark/moderation/*`, #460). Every call is\n * a POST with the ids in the body — the route table is literal and RavenDB ids contain slashes.\n *\n * A target the caller cannot see (hidden, missing, not moderatable) is the standard refusal: 404\n * (or 401 when signing in would help). A rule (own post, locked, suspended) is a 400 with `errors`,\n * a quota (votes per day) a 429 whose `result.retryAfterSeconds` says when to try again.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkModerationService {\n private readonly spark = inject(SparkService);\n\n // Vote states are fetched in batches: every widget that asks within one macrotask shares a request.\n private readonly pending = new Map<string, Map<string, ((state: ModerationVoteState | null) => void)[]>>();\n private flushScheduled = false;\n\n private readonly router = inject(Router, { optional: true });\n\n readonly #votesCast = signal(0);\n\n /** Counts the caller's accepted votes, so the own reputation badge re-reads (a downvote costs reputation). */\n readonly votesCast = this.#votesCast.asReadonly();\n\n private readonly navigated = toSignal(\n this.router?.events.pipe(filter(e => e instanceof NavigationEnd)) ?? EMPTY,\n { initialValue: null });\n\n /**\n * Changes whenever the caller's own reputation may have changed: after every navigation (a\n * sign-in, a sign-out, another page) and after the caller's own votes. The own badge and the\n * review-queue link read it to re-fetch {@link ownReputation}; a component in the app shell lives\n * for the whole session and would otherwise show the first answer until a full reload.\n */\n // A fresh object each time: returning the vote count alone would compare equal after a navigation\n // and notify nobody.\n readonly ownReputationChanged = computed(() => ({ navigation: this.navigated(), votes: this.#votesCast() }));\n\n #ownInFlight: Promise<ModerationReputation | null> | null = null;\n\n /**\n * The caller's own reputation, shared by every caller in flight at once: the badge and the\n * review-queue link both re-read on the same navigation and cost one request. `null` signed out.\n */\n ownReputation(): Promise<ModerationReputation | null> {\n return this.#ownInFlight ??= this.reputation().finally(() => this.#ownInFlight = null);\n }\n\n /** Casts (+1 / −1) or withdraws (0) the caller's vote; resolves to the target's new state. */\n async vote(type: string, id: string, direction: -1 | 0 | 1): Promise<ModerationVoteState> {\n const state = await this.spark.postEnvelope<ModerationVoteState>('/moderation/vote', { objectTypeId: type, id, direction });\n this.#votesCast.update(n => n + 1);\n return state;\n }\n\n /**\n * The score and the caller's vote for one target, batched with every other call made in the same\n * macrotask for the same type (one `POST /spark/moderation/votes`, ≤ 100 ids). Resolves to `null`\n * for an id the server left out (not visible to the caller).\n */\n voteState(type: string, id: string): Promise<ModerationVoteState | null> {\n return new Promise(resolve => {\n let byId = this.pending.get(type);\n if (!byId) this.pending.set(type, (byId = new Map()));\n const waiting = byId.get(id) ?? [];\n waiting.push(resolve);\n byId.set(id, waiting);\n if (!this.flushScheduled) {\n this.flushScheduled = true;\n setTimeout(() => this.flush(), 0);\n }\n });\n }\n\n /** `POST /spark/moderation/votes` for up to 100 ids of one type. */\n votes(type: string, ids: string[]): Promise<ModerationVoteState[]> {\n return this.spark.postEnvelope<ModerationVoteState[]>('/moderation/votes', { objectTypeId: type, ids });\n }\n\n flag(type: string, id: string, reason: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/flag', { objectTypeId: type, id, reason });\n }\n\n lock(type: string, id: string, reason?: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/lock', { objectTypeId: type, id, reason });\n }\n\n unlock(type: string, id: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/unlock', { objectTypeId: type, id });\n }\n\n /** What the moderator panel shows about a target, including which tools the caller holds. */\n status(type: string, id: string): Promise<ModerationStatus> {\n return this.spark.postEnvelope<ModerationStatus>('/moderation/status', { objectTypeId: type, id });\n }\n\n /**\n * A user's reputation; the caller's own when `userId` is omitted (with privileges and suspension).\n * `null` for an anonymous caller: the server answers it 200 with no result rather than 401, so a\n * badge on a page anyone may read never sends the app to the sign-in page.\n */\n reputation(userId?: string): Promise<ModerationReputation | null> {\n return this.spark.postEnvelope<ModerationReputation | null>('/moderation/reputation', userId ? { userId } : {});\n }\n\n /** The caller's own ledger, newest first. Voters are never named; a reversal reads \"Voting corrected (−N)\". */\n history(take = 100): Promise<ModerationReputationLine[]> {\n return this.spark.postEnvelope<ModerationReputationLine[]>('/moderation/reputation/history', { take });\n }\n\n cases(status: string = 'open', skip = 0, take = 50): Promise<ModerationCaseSummary[]> {\n return this.spark.postEnvelope<ModerationCaseSummary[]>('/moderation/cases', { status, skip, take });\n }\n\n case(caseId: string): Promise<ModerationCaseDetail> {\n return this.spark.postEnvelope<ModerationCaseDetail>('/moderation/case', { caseId });\n }\n\n /** `uphold` / `decline` (flag cases), `dismiss` / `reverse` / `merge` / `suspend` (fraud cases; the last two name `accountId`). */\n decide(caseId: string, decision: string, accountId?: string, reason?: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/case/decide', { caseId, decision, accountId, reason });\n }\n\n suspend(userId: string, days?: number | null, reason?: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/suspend', { userId, days: days ?? null, reason });\n }\n\n unsuspend(userId: string): Promise<void> {\n return this.spark.postEnvelope<void>('/moderation/unsuspend', { userId });\n }\n\n audit(skip = 0, take = 50): Promise<ModerationAuditEntry[]> {\n return this.spark.postEnvelope<ModerationAuditEntry[]>('/moderation/audit', { skip, take });\n }\n\n private flush(): void {\n this.flushScheduled = false;\n const batches = [...this.pending.entries()];\n this.pending.clear();\n for (const [type, byId] of batches) {\n const ids = [...byId.keys()];\n for (let i = 0; i < ids.length; i += 100) {\n const chunk = ids.slice(i, i + 100);\n this.votes(type, chunk).then(\n states => {\n const found = new Map(states.map(s => [s.id, s] as const));\n for (const id of chunk) byId.get(id)?.forEach(resolve => resolve(found.get(id) ?? null));\n },\n () => {\n for (const id of chunk) byId.get(id)?.forEach(resolve => resolve(null));\n });\n }\n }\n }\n}\n","import { InjectionToken } from '@angular/core';\n\n/** Where the review queue is routed; the moderator panel links there. Set by {@link provideSparkModeration}. */\nexport const SPARK_MODERATION_REVIEW_PATH = new InjectionToken<string>('SparkModerationReviewPath', {\n factory: () => '/moderation/review',\n});\n","import { ChangeDetectionStrategy, Component, computed, effect, inject, input, signal } from '@angular/core';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { EntityAttributeDefinition, PersistentObject, QueryResultItem, SparkCellColumn } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationVoteState } from './spark-moderation.models';\n\n/**\n * The vote widget (#460): up, score, down. An attribute renderer — register it with\n * {@link sparkModerationRenderers} and give an attribute of an `IModeratable` type\n * `\"renderer\": \"spark-vote\"`. The attribute's own value is ignored: the score comes from\n * `/spark/moderation/votes` (batched per page), never from the document, so a vote never moves the\n * row's etag.\n *\n * An arrow the caller holds no right for (`canUpvote` / `canDownvote` false on the state) is\n * disabled, so a user without the privilege sees the score but is not offered a click that the server\n * refuses — except the active arrow, since withdrawing a vote needs no right.\n *\n * On the detail page the entity type is the object's. In a query grid a row carries no type, so the\n * column needs `\"rendererOptions\": { \"type\": \"<entity type id or alias>\" }`.\n */\n@Component({\n selector: 'spark-vote',\n imports: [TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (state(); as s) {\n <span class=\"spark-vote d-inline-flex align-items-center gap-1\" [class.spark-vote-locked]=\"s.locked\">\n <button type=\"button\" class=\"btn btn-sm spark-vote-up\" [class.btn-primary]=\"s.myVote === 1\" [class.btn-outline-secondary]=\"s.myVote !== 1\"\n [disabled]=\"busy() || s.locked || (s.canUpvote === false && s.myVote !== 1)\" [attr.aria-pressed]=\"s.myVote === 1\" [attr.aria-label]=\"'moderation.upvote' | t\" (click)=\"cast(1)\">▲</button>\n <span class=\"spark-vote-score fw-bold\" [attr.aria-label]=\"'moderation.score' | t\">{{ s.score }}</span>\n <button type=\"button\" class=\"btn btn-sm spark-vote-down\" [class.btn-danger]=\"s.myVote === -1\" [class.btn-outline-secondary]=\"s.myVote !== -1\"\n [disabled]=\"busy() || s.locked || (s.canDownvote === false && s.myVote !== -1)\" [attr.aria-pressed]=\"s.myVote === -1\" [attr.aria-label]=\"'moderation.downvote' | t\" (click)=\"cast(-1)\">▼</button>\n @if (error(); as message) {\n <span class=\"text-danger small spark-vote-error\" role=\"alert\">{{ message }}</span>\n }\n </span>\n }\n `,\n})\nexport class SparkVoteComponent {\n private readonly moderation = inject(SparkModerationService);\n\n // Renderer inputs (all optional: the hosts pass only what a renderer declares).\n value = input<unknown>();\n attribute = input<EntityAttributeDefinition | undefined>();\n column = input<SparkCellColumn | undefined>();\n options = input<Record<string, any> | undefined>();\n item = input<PersistentObject | QueryResultItem | Record<string, any> | undefined>();\n\n protected readonly state = signal<ModerationVoteState | null>(null);\n protected readonly busy = signal(false);\n protected readonly error = signal<string | null>(null);\n\n protected readonly target = computed(() => {\n const item = this.item() as Record<string, any> | undefined;\n const id = item?.['id'] as string | undefined;\n const type = (item?.['objectTypeId'] as string | undefined) ?? (this.options()?.['type'] as string | undefined);\n return id && type ? { id, type } : null;\n });\n\n constructor() {\n effect(() => {\n const target = this.target();\n if (!target) return;\n this.moderation.voteState(target.type, target.id).then(s => this.state.set(s));\n });\n }\n\n async cast(direction: 1 | -1): Promise<void> {\n const target = this.target();\n const current = this.state();\n if (!target || !current || this.busy()) return;\n // Clicking the active arrow withdraws the vote.\n const next = current.myVote === direction ? 0 : direction;\n this.busy.set(true);\n this.error.set(null);\n try {\n this.state.set(await this.moderation.vote(target.type, target.id, next));\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n\n/** A readable message for a refused moderation call (400 errors, 429 quota, 404 refusal). */\nexport function describeModerationError(e: unknown): string {\n const err = e as HttpErrorResponse;\n const body = err?.error as { result?: { errors?: { errorMessage?: unknown }[]; error?: string } } | undefined;\n const first = body?.result?.errors?.[0]?.errorMessage;\n if (typeof first === 'string') return first;\n if (first && typeof first === 'object') {\n const values = Object.values(first as Record<string, string>);\n if (values.length) return values[0];\n }\n return body?.result?.error ?? err?.message ?? 'Failed';\n}\n","import { ChangeDetectionStrategy, Component, inject, input, signal } from '@angular/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { SparkDetailContext } from '@mintplayer/ng-spark/panels';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { describeModerationError } from './spark-vote.component';\n\n/**\n * **Flag** on the detail page of every object (#460). Registered by {@link provideSparkModeration}\n * as a `SPARK_DETAIL_ACTIONS` entry. The button is offered to every signed-in viewer of a live row;\n * the server decides (`Flag/T`, a visible moderatable row) and answers a refusal inline — the\n * permissions endpoint does not report moderation rights, and a type that is not moderatable answers\n * like a missing row, so the button cannot know in advance.\n */\n@Component({\n selector: 'spark-flag-button',\n imports: [TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (context().deleted !== 'only') {\n <span class=\"d-inline-flex gap-2 align-items-center\">\n <button type=\"button\" class=\"btn btn-outline-warning spark-flag\" [disabled]=\"busy() || done()\" (click)=\"flag()\">\n {{ 'moderation.flag' | t }}\n </button>\n @if (done()) {\n <span class=\"small text-success spark-flag-done\">{{ 'moderation.flagged' | t }}</span>\n }\n @if (error(); as message) {\n <span class=\"small text-danger spark-flag-error\" role=\"alert\">{{ message }}</span>\n }\n </span>\n }\n `,\n})\nexport class SparkFlagButtonComponent {\n private readonly moderation = inject(SparkModerationService);\n private readonly lang = inject(SparkLanguageService);\n\n context = input.required<SparkDetailContext>();\n\n protected readonly busy = signal(false);\n protected readonly done = signal(false);\n protected readonly error = signal<string | null>(null);\n\n async flag(): Promise<void> {\n const reason = prompt(this.lang.t('moderation.flagReason'));\n if (!reason?.trim()) return;\n const ctx = this.context();\n this.busy.set(true);\n this.error.set(null);\n try {\n await this.moderation.flag(ctx.type, ctx.id, reason.trim());\n this.done.set(true);\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, effect, inject, input, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { SparkDetailContext } from '@mintplayer/ng-spark/panels';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationStatus } from './spark-moderation.models';\nimport { describeModerationError } from './spark-vote.component';\nimport { SPARK_MODERATION_REVIEW_PATH } from './spark-moderation.tokens';\n\n/**\n * The moderator panel under a moderatable object's detail page (#460): lock state and Lock/Unlock\n * (`Lock/T`), open flags with a link to the review queue (`Review/Moderation`), and Suspend author\n * (`Suspend/Moderation`). Restore/Purge and Revert are the SoftDelete and History entry points'.\n * Renders nothing for a caller with none of these rights, or for a type that is not moderatable.\n */\n@Component({\n selector: 'spark-moderator-panel',\n imports: [BsCardComponent, BsCardHeaderComponent, TranslateKeyPipe, RouterLink],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (status(); as s) {\n @if (s.canLock || s.canReview || s.canSuspend) {\n <bs-card class=\"d-block spark-moderator-panel\">\n <bs-card-header>{{ 'moderation.panel' | t }}</bs-card-header>\n <div class=\"p-3 d-flex flex-wrap gap-2 align-items-center\">\n @if (s.locked) {\n <span class=\"badge bg-secondary spark-moderation-locked\">{{ 'moderation.locked' | t }}</span>\n @if (s.lockReason) { <span class=\"small text-muted\">{{ s.lockReason }}</span> }\n }\n @if (s.canLock) {\n @if (s.locked) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-unlock\" [disabled]=\"busy()\" (click)=\"unlock()\">{{ 'moderation.unlock' | t }}</button>\n } @else {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-lock\" [disabled]=\"busy()\" (click)=\"lock()\">{{ 'moderation.lock' | t }}</button>\n }\n }\n @if (s.canReview && s.openFlags > 0) {\n <a class=\"btn btn-sm btn-outline-warning spark-open-flags\" [routerLink]=\"reviewPath\" [queryParams]=\"{ case: s.openCaseId }\">\n {{ 'moderation.openFlags' | t }}: {{ s.openFlags }}\n </a>\n }\n @if (s.canSuspend && s.authorId) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger spark-suspend-author\" [disabled]=\"busy()\" (click)=\"suspendAuthor(s.authorId)\">{{ 'moderation.suspendAuthor' | t }}</button>\n }\n @if (error(); as message) {\n <span class=\"small text-danger\" role=\"alert\">{{ message }}</span>\n }\n </div>\n </bs-card>\n }\n }\n `,\n})\nexport class SparkModeratorPanelComponent {\n private readonly moderation = inject(SparkModerationService);\n private readonly lang = inject(SparkLanguageService);\n protected readonly reviewPath = inject(SPARK_MODERATION_REVIEW_PATH);\n\n context = input.required<SparkDetailContext>();\n\n protected readonly status = signal<ModerationStatus | null>(null);\n protected readonly busy = signal(false);\n protected readonly error = signal<string | null>(null);\n\n constructor() {\n effect(() => {\n const ctx = this.context();\n if (ctx.deleted === 'only') {\n this.status.set(null);\n return;\n }\n // A refusal (not moderatable, not visible) simply hides the panel.\n this.moderation.status(ctx.type, ctx.id).then(s => this.status.set(s), () => this.status.set(null));\n });\n }\n\n async lock(): Promise<void> {\n const reason = prompt(this.lang.t('moderation.lockReason')) ?? undefined;\n await this.run(() => this.moderation.lock(this.context().type, this.context().id, reason));\n }\n\n async unlock(): Promise<void> {\n await this.run(() => this.moderation.unlock(this.context().type, this.context().id));\n }\n\n async suspendAuthor(userId: string): Promise<void> {\n const answer = prompt(this.lang.t('moderation.suspendDays'));\n if (answer === null) return;\n const days = answer.trim() === '' ? null : Number(answer);\n if (days !== null && !(days > 0)) return;\n await this.run(() => this.moderation.suspend(userId, days));\n }\n\n private async run(work: () => Promise<void>): Promise<void> {\n this.busy.set(true);\n this.error.set(null);\n try {\n await work();\n const ctx = this.context();\n this.status.set(await this.moderation.status(ctx.type, ctx.id));\n await ctx.reload();\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, effect, inject, input, signal } from '@angular/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationReputation } from './spark-moderation.models';\n\n/**\n * A user's reputation (#460): `<spark-reputation-badge [userId]=\"post.authorId\" />`, or without\n * `userId` the signed-in user's own, with the not-yet-credited part (delayed crediting) beside it.\n *\n * The own badge usually sits in the app shell, which lives for the whole session: it re-reads the\n * reputation after every navigation (a sign-in, a sign-out, opening another page) and after the\n * caller's own votes, or it would show the first value it read until a full page reload.\n */\n@Component({\n selector: 'spark-reputation-badge',\n imports: [TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (reputation(); as r) {\n <span class=\"badge bg-body-tertiary text-body border spark-reputation-badge\" [attr.title]=\"'moderation.reputation' | t\">\n {{ r.total }}\n @if (!userId() && r.pending) {\n <!-- A received downvote makes the pending part negative: \"(-2 pending)\", never \"(+-2 pending)\". -->\n <span class=\"text-muted spark-reputation-pending\">({{ r.pending > 0 ? '+' : '' }}{{ r.pending }} {{ 'moderation.pending' | t }})</span>\n }\n </span>\n }\n `,\n})\nexport class SparkReputationBadgeComponent {\n private readonly moderation = inject(SparkModerationService);\n\n userId = input<string | null | undefined>();\n\n protected readonly reputation = signal<ModerationReputation | null>(null);\n\n constructor() {\n effect(() => {\n const userId = this.userId() ?? undefined;\n // The own badge only: someone else's total does not move because the viewer navigated.\n const read = userId\n ? this.moderation.reputation(userId)\n : (this.moderation.ownReputationChanged(), this.moderation.ownReputation());\n read.then(r => this.reputation.set(r), () => this.reputation.set(null));\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { DatePipe } from '@angular/common';\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { EntityType } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkModerationService } from './spark-moderation.service';\nimport { ModerationCaseDetail, ModerationCaseSummary } from './spark-moderation.models';\nimport { describeModerationError } from './spark-vote.component';\n\n/**\n * The review queue (#460): open cases on the left, the selected case's review surface on the right —\n * the voter→target matrix, the timeline against the posts' creation, the accounts side by side (age,\n * registration method, how many accounts share a network with it, reputation now and after reversing)\n * and the rule that fired — with the decisions the case allows. Needs `Review/Moderation`; merge and\n * suspend also `Suspend/Moderation` (the server refuses otherwise).\n *\n * Routed by {@link sparkModerationRoutes} at `moderation/review`; `?case=` selects a case.\n */\n@Component({\n selector: 'spark-review-queue',\n imports: [BsCardComponent, BsCardHeaderComponent, RouterLink, TranslateKeyPipe, DatePipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"container-fluid spark-review-queue\">\n <h1 class=\"h3\">{{ 'moderation.reviewQueue' | t }}</h1>\n @if (error(); as message) { <div class=\"alert alert-danger\" role=\"alert\">{{ message }}</div> }\n <div class=\"row g-3\">\n <div class=\"col-md-4\">\n @for (c of cases(); track c.id) {\n <button type=\"button\" class=\"list-group-item list-group-item-action w-100 text-start p-2 border mb-1 spark-case\"\n [class.active]=\"selected()?.case?.id === c.id\" (click)=\"open(c.id)\">\n <strong>{{ c.kind }}</strong>\n @if (c.flagCount) { <span class=\"badge text-bg-warning ms-1\">{{ c.flagCount }}</span> }\n @if (targets()[c.id]?.title; as title) { <div class=\"small fw-semibold text-truncate spark-case-target\">{{ title }}</div> }\n <div class=\"small\">{{ c.summary }}</div>\n <div class=\"small text-muted\">{{ c.openedAtUtc | date: 'short' }}</div>\n </button>\n } @empty {\n <p class=\"text-muted spark-no-cases\">{{ 'moderation.noCases' | t }}</p>\n }\n </div>\n <div class=\"col-md-8\">\n @if (selected(); as d) {\n <bs-card class=\"d-block spark-case-detail\">\n <bs-card-header>{{ 'moderation.rule' | t }}: {{ d.case.kind }} — {{ d.case.summary }}</bs-card-header>\n <div class=\"p-3\">\n @if (targets()[d.case.id]; as target) {\n <p class=\"spark-case-target-link\">\n @if (target.link) { <a [routerLink]=\"target.link\">{{ target.title ?? d.case.targetId }}</a> }\n @else { {{ target.title ?? d.case.targetId }} }\n </p>\n }\n @if (d.accounts.length) {\n <h2 class=\"h6\">{{ 'moderation.accounts' | t }}</h2>\n <table class=\"table table-sm spark-case-accounts\">\n <thead><tr><th></th><th>{{ 'moderation.ageDays' | t }}</th><th></th><th>{{ 'moderation.sharesNetwork' | t }}</th><th>{{ 'moderation.repBefore' | t }}</th><th>{{ 'moderation.repAfter' | t }}</th></tr></thead>\n <tbody>\n @for (a of d.accounts; track a.id) {\n <tr><td>{{ a.id }}</td><td>{{ a.ageDays }}</td><td>{{ a.registrationMethod }}</td><td>{{ a.sharesNetworkWith }}</td><td>{{ a.reputationBefore }}</td><td>{{ a.reputationAfter }}</td></tr>\n }\n </tbody>\n </table>\n }\n @if (d.matrix.length) {\n <h2 class=\"h6\">{{ 'moderation.matrix' | t }}</h2>\n <ul class=\"small spark-case-matrix\">\n @for (m of d.matrix; track m.voterId + m.authorId) { <li>{{ m.voterId }} → {{ m.authorId }}: {{ m.votes }}</li> }\n </ul>\n }\n @if (d.timeline.length) {\n <h2 class=\"h6\">{{ 'moderation.timeline' | t }}</h2>\n <table class=\"table table-sm small spark-case-timeline\">\n <tbody>\n @for (t of d.timeline; track t.voteId) {\n <tr><td>{{ t.castAtUtc | date: 'short' }}</td><td>{{ t.voterId }} → {{ t.authorId }}</td><td>{{ t.direction > 0 ? '▲' : t.direction < 0 ? '▼' : '·' }}</td><td>{{ t.secondsAfterPost ?? '' }} s</td></tr>\n }\n </tbody>\n </table>\n }\n @if (d.flags.length) {\n <ul class=\"small spark-case-flags\">\n @for (f of d.flags; track f.flaggerId) { <li>{{ f.reason }} <span class=\"text-muted\">({{ f.raisedAtUtc | date: 'short' }})</span></li> }\n </ul>\n }\n @if (d.case.status === 'open') {\n <div class=\"d-flex flex-wrap gap-2\">\n @if (d.case.kind === 'flag') {\n <button type=\"button\" class=\"btn btn-sm btn-success spark-decide-uphold\" [disabled]=\"busy()\" (click)=\"decide('uphold')\">{{ 'moderation.uphold' | t }}</button>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-decide-decline\" [disabled]=\"busy()\" (click)=\"decide('decline')\">{{ 'moderation.decline' | t }}</button>\n } @else {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary spark-decide-dismiss\" [disabled]=\"busy()\" (click)=\"decide('dismiss')\">{{ 'moderation.dismiss' | t }}</button>\n <button type=\"button\" class=\"btn btn-sm btn-warning spark-decide-reverse\" [disabled]=\"busy()\" (click)=\"decide('reverse')\">{{ 'moderation.reverse' | t }}</button>\n @for (a of d.case.accountIds; track a) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger spark-decide-merge\" [disabled]=\"busy()\" (click)=\"decide('merge', a)\">{{ 'moderation.merge' | t }}: {{ a }}</button>\n <button type=\"button\" class=\"btn btn-sm btn-danger spark-decide-suspend\" [disabled]=\"busy()\" (click)=\"decide('suspend', a)\">{{ 'moderation.suspend' | t }}: {{ a }}</button>\n }\n }\n </div>\n }\n </div>\n </bs-card>\n }\n </div>\n </div>\n </div>\n `,\n})\nexport class SparkReviewQueueComponent implements OnInit {\n private readonly moderation = inject(SparkModerationService);\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n\n private readonly spark = inject(SparkService);\n\n protected readonly cases = signal<ModerationCaseSummary[]>([]);\n /** Per case id: the target post's title and route, once resolved. */\n protected readonly targets = signal<Record<string, { title: string | null; link: string[] | null }>>({});\n protected readonly selected = signal<ModerationCaseDetail | null>(null);\n protected readonly busy = signal(false);\n protected readonly error = signal<string | null>(null);\n\n async ngOnInit(): Promise<void> {\n await this.load();\n const requested = this.route.snapshot.queryParamMap.get('case');\n if (requested) await this.open(requested);\n }\n\n async open(caseId: string): Promise<void> {\n await this.run(async () => {\n this.selected.set(await this.moderation.case(caseId));\n await this.router.navigate([], { relativeTo: this.route, queryParams: { case: caseId }, replaceUrl: true });\n });\n }\n\n async decide(decision: string, accountId?: string): Promise<void> {\n const current = this.selected();\n if (!current) return;\n await this.run(async () => {\n await this.moderation.decide(current.case.id, decision, accountId);\n this.selected.set(await this.moderation.case(current.case.id));\n await this.load();\n });\n }\n\n private async load(): Promise<void> {\n await this.run(async () => this.cases.set(await this.moderation.cases('open')));\n void this.resolveTargets(this.cases());\n }\n\n /**\n * Names each case's post: its title (the object's breadcrumb) and a link to it. A case carries only\n * the target's id and type name; without this the queue listed \"flag · Spam\" with no way to tell\n * which post it was about. A target the caller cannot open (purged, hidden) keeps just its id.\n */\n private async resolveTargets(cases: ModerationCaseSummary[]): Promise<void> {\n const pending = cases.filter(c => c.targetId && c.targetType && !(c.id in this.targets()));\n if (!pending.length) return;\n let types: EntityType[] = [];\n try {\n types = await this.spark.getEntityTypes();\n } catch {\n return;\n }\n await Promise.all(pending.map(async c => {\n const type = types.find(t => t.name === c.targetType || t.id === c.targetType || t.alias === c.targetType);\n const route = type ? type.alias || type.id : null;\n let title: string | null = null;\n if (route) {\n try {\n const po = await this.spark.get(route, c.targetId!);\n title = po.breadcrumb || null;\n } catch {\n // Not visible to the caller (or gone): the id stays the label, without a link.\n }\n }\n const link = route && title ? ['/po', route, c.targetId!] : null;\n this.targets.update(t => ({ ...t, [c.id]: { title, link } }));\n }));\n }\n\n private async run(work: () => Promise<void>): Promise<void> {\n this.busy.set(true);\n this.error.set(null);\n try {\n await work();\n } catch (e) {\n this.error.set(describeModerationError(e));\n } finally {\n this.busy.set(false);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, effect, inject, input, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkModerationService } from './spark-moderation.service';\n\n/**\n * A link to the review queue (#460) that renders only for a caller who may review — earned, or\n * granted by a group such as moderators (`canReview` on the caller's own reputation). Anyone else\n * would open a page whose every request answers 404. Re-checked after each navigation, so it appears\n * on sign-in and disappears on sign-out.\n *\n * `<spark-review-queue-link linkClass=\"btn btn-sm btn-link\" />` — `linkClass` styles the anchor.\n */\n@Component({\n selector: 'spark-review-queue-link',\n imports: [RouterLink, TranslateKeyPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[style.display]': 'canReview() ? null : \"none\"' },\n template: `\n @if (canReview()) {\n <a class=\"spark-review-queue-link\" [class]=\"linkClass()\" routerLink=\"/moderation/review\">{{ 'moderation.reviewQueue' | t }}</a>\n }\n `,\n})\nexport class SparkReviewQueueLinkComponent {\n private readonly moderation = inject(SparkModerationService);\n\n /** Classes for the anchor itself (the host element is only a wrapper). */\n linkClass = input<string>('');\n\n protected readonly canReview = signal(false);\n\n constructor() {\n effect(() => {\n this.moderation.ownReputationChanged();\n this.moderation.ownReputation().then(r => this.canReview.set(r?.canReview === true), () => this.canReview.set(false));\n });\n }\n}\n","import { Provider } from '@angular/core';\nimport { Routes } from '@angular/router';\nimport { SparkAttributeRendererRegistration } from '@mintplayer/ng-spark/renderers';\nimport { provideSparkDetailActions, provideSparkDetailPanels } from '@mintplayer/ng-spark/panels';\nimport { SparkFlagButtonComponent } from './spark-flag-button.component';\nimport { SparkModeratorPanelComponent } from './spark-moderator-panel.component';\nimport { SparkReviewQueueComponent } from './spark-review-queue.component';\nimport { SparkVoteComponent } from './spark-vote.component';\nimport { SPARK_MODERATION_REVIEW_PATH } from './spark-moderation.tokens';\n\n/**\n * Adds the Moderation UI (#460) to the pages `sparkRoutes()` routes to: a **Flag** action on every\n * detail page and the **moderator panel** under it. Needs the server package\n * `MintPlayer.Spark.Moderation`.\n *\n * ```ts\n * providers: [\n * provideSpark(...),\n * provideSparkModeration(),\n * provideSparkAttributeRenderers([...sparkModerationRenderers, ...yourRenderers]),\n * ]\n * // routes: [...sparkModerationRoutes(), ...sparkRoutes()]\n * ```\n *\n * The vote widget is an attribute renderer (`\"renderer\": \"spark-vote\"` on an attribute of an\n * `IModeratable` type), so it is registered through {@link sparkModerationRenderers} with the app's\n * own renderers — `provideSparkAttributeRenderers` takes one list.\n */\nexport function provideSparkModeration(options?: { reviewPath?: string; panelOrder?: number }): Provider[] {\n return [\n ...provideSparkDetailActions({ id: 'spark-moderation-flag', component: SparkFlagButtonComponent, priority: 90 }),\n ...provideSparkDetailPanels({ id: 'spark-moderation-panel', component: SparkModeratorPanelComponent, order: options?.panelOrder ?? 90 }),\n { provide: SPARK_MODERATION_REVIEW_PATH, useValue: options?.reviewPath ?? '/moderation/review' },\n ];\n}\n\n/** The vote widget as an attribute renderer named `spark-vote` (detail page and grid column). */\nexport const sparkModerationRenderers: SparkAttributeRendererRegistration[] = [\n { name: 'spark-vote', detailComponent: SparkVoteComponent, columnComponent: SparkVoteComponent },\n];\n\n/**\n * The review-queue page at `moderation/review`. Put it **before** `sparkRoutes()` — a literal first\n * segment is fine anywhere, but keeping add-on routes first avoids surprises with parameterised ones.\n */\nexport function sparkModerationRoutes(path = 'moderation/review'): Routes {\n return [{ path, component: SparkReviewQueueComponent }];\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { Pipe, inject } from "@angular/core";
|
|
3
3
|
import { SparkLanguageService } from "@mintplayer/ng-spark/services";
|
|
4
|
-
import { AS_DETAIL_BREADCRUMBS_KEY, ELookupDisplayType, isReservedAsDetailKey, nestedPoToDict, nestedPoToDisplayRow, resolveTranslation, resolvedBreadcrumb, selfBreadcrumb, valueFor } from "@mintplayer/ng-spark/models";
|
|
4
|
+
import { AS_DETAIL_BREADCRUMBS_KEY, ELookupDisplayType, ShowedOn, hasShowedOnFlag, isReservedAsDetailKey, nestedPoToDict, nestedPoToDisplayRow, resolveTranslation, resolvedBreadcrumb, selfBreadcrumb, valueFor } from "@mintplayer/ng-spark/models";
|
|
5
5
|
import { isRowRendered, resolveRowRenderer } from "@mintplayer/ng-spark/renderers";
|
|
6
6
|
var TranslateKeyPipe = class TranslateKeyPipe {
|
|
7
7
|
lang = inject(SparkLanguageService);
|
|
@@ -515,7 +515,7 @@ var AsDetailColumnsPipe = class AsDetailColumnsPipe {
|
|
|
515
515
|
transform(attr, asDetailTypes, renderers) {
|
|
516
516
|
const type = asDetailTypes[attr.name];
|
|
517
517
|
if (!type) return [];
|
|
518
|
-
return type.attributes.filter((a) => a
|
|
518
|
+
return type.attributes.filter((a) => isDrawnSomewhere(a) && !isRowRendered(a, renderers)).sort((a, b) => a.order - b.order);
|
|
519
519
|
}
|
|
520
520
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
521
521
|
minVersion: "12.0.0",
|
|
@@ -548,6 +548,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
548
548
|
}]
|
|
549
549
|
}]
|
|
550
550
|
});
|
|
551
|
+
function isDrawnSomewhere(attr) {
|
|
552
|
+
return hasShowedOnFlag(attr.showedOn, ShowedOn.Query) || hasShowedOnFlag(attr.showedOn, ShowedOn.PersistentObject);
|
|
553
|
+
}
|
|
551
554
|
var AsDetailRowRendererPipe = class AsDetailRowRendererPipe {
|
|
552
555
|
transform(attr, asDetailTypes, renderers) {
|
|
553
556
|
return resolveRowRenderer(asDetailTypes[attr.name], renderers);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark-pipes.mjs","names":[],"sources":["../../pipes/src/translate-key.pipe.ts","../../pipes/src/resolve-translation.pipe.ts","../../pipes/src/input-type.pipe.ts","../../pipes/src/parsed-date.pipe.ts","../../pipes/src/apply-field-template.ts","../../pipes/src/attribute-value.pipe.ts","../../pipes/src/raw-attribute-value.pipe.ts","../../pipes/src/reference-display-value.pipe.ts","../../pipes/src/reference-attr-value.pipe.ts","../../pipes/src/reference-link-route.pipe.ts","../../pipes/src/reference-chips.pipe.ts","../../pipes/src/router-link.pipe.ts","../../pipes/src/as-detail-type.pipe.ts","../../pipes/src/as-detail-columns.pipe.ts","../../pipes/src/as-detail-row-renderer.pipe.ts","../../pipes/src/as-detail-cell-value.pipe.ts","../../pipes/src/as-detail-display-value.pipe.ts","../../pipes/src/can-create-detail-row.pipe.ts","../../pipes/src/can-delete-detail-row.pipe.ts","../../pipes/src/can-edit-detail-row.pipe.ts","../../pipes/src/lookup-display-type.pipe.ts","../../pipes/src/lookup-display-value.pipe.ts","../../pipes/src/lookup-options.pipe.ts","../../pipes/src/inline-ref-options.pipe.ts","../../pipes/src/error-for-attribute.pipe.ts","../../pipes/src/icon-name.pipe.ts","../../pipes/src/array-value.pipe.ts","../../pipes/src/query-cell.pipe.ts"],"sourcesContent":["import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\n\n@Pipe({ name: 't', pure: false, standalone: true })\nexport class TranslateKeyPipe implements PipeTransform {\n private readonly lang = inject(SparkLanguageService);\n\n transform(key: string): string {\n return this.lang.t(key);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { TranslatedString, resolveTranslation } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'resolveTranslation', standalone: true, pure: false })\nexport class ResolveTranslationPipe implements PipeTransform {\n transform(value: TranslatedString | undefined, fallback?: string): string {\n return resolveTranslation(value) || fallback || '';\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({ name: 'inputType', standalone: true, pure: true })\nexport class InputTypePipe implements PipeTransform {\n transform(dataType: string): string {\n switch (dataType) {\n case 'number':\n case 'decimal':\n return 'number';\n case 'boolean':\n return 'checkbox';\n case 'datetime':\n return 'datetime-local';\n case 'date':\n return 'date';\n case 'color':\n return 'color';\n case 'url':\n case 'image':\n // Both hold a URL, so `url` gets the browser's own validation and keyboard. `image` is a\n // link to an image rather than an upload: the value is still a string the author types.\n return 'url';\n default:\n return 'text';\n }\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n/**\n * Parses a wire value into a `Date`, or `null` when it is absent or unparseable.\n *\n * Exists so the detail page and the grid agree. The grid gets its `Date` from `queryCellValue`, which\n * needs a cell and a column and so cannot be reused on a detail attribute; before this, the detail page\n * had no parsing step at all and printed the raw ISO string — `2026-12-31T23:59:00-08:00` in a\n * definition list, while the grid showed `01/01/2027, 08:59` for the same document.\n *\n * The parsing contract is deliberately identical to `queryCellValue`'s: `new Date(...)`, and an\n * unparseable value yields `null` so the caller can fall back to its own text rather than rendering\n * \"Invalid Date\".\n *\n * Note what `new Date(...)` does to an offset, because it is the whole reason this is a separate step:\n * it collapses the value to an instant and forgets the offset it was written with. That is correct for\n * display — a viewer wants the moment in their own zone — but it means a `Date` can never be the thing\n * you reach for when you need the *originating* wall clock. Read that from the wire value instead.\n */\n@Pipe({ name: 'parsedDate', standalone: true, pure: true })\nexport class ParsedDatePipe implements PipeTransform {\n transform(value: unknown): Date | null {\n if (value == null || value === '') return null;\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (typeof value !== 'string' && typeof value !== 'number') return null;\n\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n }\n}\n","/**\n * Fills a `{Field}` template from a plain object's own fields. Used ONLY for AsDetail\n * nested-object summary display: an embedded value object (no id, no document) is rendered\n * client-side from its own fields. This is deliberately distinct from entity/reference\n * breadcrumbs — those are resolved recursively on the server (BreadcrumbResolver) and the\n * client only reads the resulting strings. Unknown or null placeholders render empty.\n */\nexport function applyFieldTemplate(template: string, data: Record<string, any>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_match, propertyName) => {\n const value = data[propertyName];\n return value != null ? String(value) : '';\n });\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType, LookupReference, PersistentObject, isReservedAsDetailKey, nestedPoToDict, resolveTranslation, resolvedBreadcrumb } from '@mintplayer/ng-spark/models';\nimport { applyFieldTemplate } from './apply-field-template';\n\n@Pipe({ name: 'attributeValue', standalone: true, pure: true })\nexport class AttributeValuePipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null, entityType: EntityType | null, lookupRefOptions: Record<string, LookupReference>, allEntityTypes: EntityType[]): any {\n if (!item) return '';\n const attr = item.attributes.find(a => a.name === attrName);\n if (!attr) return '';\n\n if (attr.breadcrumb) return attr.breadcrumb;\n\n const attrDef = entityType?.attributes.find(a => a.name === attrName);\n if (attrDef?.dataType === 'AsDetail') {\n // Server emits nested PO(s) in attr.objects (array) / attr.object (single) — attr.value is null.\n if (attr.isArray) {\n const count = attr.objects?.length ?? 0;\n if (count === 0) return '';\n return `${count} item${count !== 1 ? 's' : ''}`;\n }\n if (attr.object) {\n // The server has already resolved this object's breadcrumb, and it can resolve templates\n // this side cannot. A type's breadcrumb may name a computed property that `[IgnoreProperty]`\n // keeps out of the model — HR's `Address.Crumb` is exactly that, `[Breadcrumb, IgnoreProperty]`\n // — so the template `{Crumb}` has no matching attribute here and never will. Recomputing it\n // client-side produced an empty string and fell through to \"(object)\" while the correct\n // \"Voorbeeldstraat 1, 1000 Brussel\" sat unread on `attr.object.breadcrumb`.\n //\n // One value on that property is NOT a breadcrumb: when the template renders blank the\n // server substitutes the CLR type name, and printing it reads as data — a `Build` whose\n // `Feedback.State` was unset showed the literal \"BuildFeedback\" (#384). Filtering it here\n // lets the fallbacks below run, which is what they are for.\n const resolved = resolvedBreadcrumb(attr.object.breadcrumb, attrDef.asDetailType);\n if (resolved) return resolved;\n return this.formatAsDetailValue(attrDef, nestedPoToDict(attr.object), allEntityTypes);\n }\n }\n\n if (attrDef?.lookupReferenceType && attr.value != null && attr.value !== '') {\n const lookupRef = lookupRefOptions[attrDef.lookupReferenceType];\n if (lookupRef) {\n const option = lookupRef.values.find(v => v.key === String(attr.value));\n if (option) {\n return resolveTranslation(option.values) || option.key;\n }\n }\n }\n\n if (attrDef?.dataType === 'boolean') {\n return attr.value ?? null;\n }\n\n return attr.value ?? '';\n }\n\n private formatAsDetailValue(attrDef: EntityAttributeDefinition, value: Record<string, any>, allEntityTypes: EntityType[]): string {\n const asDetailType = allEntityTypes.find(t => t.clrType === attrDef.asDetailType);\n\n if (asDetailType?.breadcrumb) {\n const result = applyFieldTemplate(asDetailType.breadcrumb, value);\n if (result && result.trim()) return result;\n }\n\n // Last resort, reached when the type declares no breadcrumb or its template resolved to\n // nothing. Joining the scalar values is always more use to a reader than \"(object)\", which\n // named the failure rather than the row and looked identical for every unresolvable cell.\n // Reserved keys are skipped: `nestedPoToDict` stashes the row key and the row's own breadcrumb\n // in this same dict, and joining those prints a guid and a placeholder as though they were\n // fields. Previously masked -- this branch was only reachable when the server sent no\n // breadcrumb, which is exactly when there was nothing stashed to leak.\n return Object.entries(value)\n .filter(([k, v]) => !isReservedAsDetailKey(k) && v != null && typeof v !== 'object' && String(v).trim() !== '')\n .map(([, v]) => v)\n .join(', ');\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { PersistentObject } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'rawAttributeValue', standalone: true, pure: true })\nexport class RawAttributeValuePipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null): any {\n return item?.attributes.find(a => a.name === attrName)?.value;\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { EntityAttributeDefinition, PersistentObject, QueryResultItem } from '@mintplayer/ng-spark/models';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\n\n@Pipe({ name: 'referenceDisplayValue', standalone: true, pure: true })\nexport class ReferenceDisplayValuePipe implements PipeTransform {\n private readonly lang = inject(SparkLanguageService);\n\n transform(attr: EntityAttributeDefinition, formData: Record<string, any>, referenceOptions: Record<string, QueryResultItem[]>): string {\n const selectedId = formData[attr.name];\n if (!selectedId) return this.lang.t('common.notSelected');\n\n const options = referenceOptions[attr.name] || [];\n const selected = options.find(o => o.id === selectedId);\n // The server resolves a row's display string into `breadcrumb`; falling back to the id\n // beats showing nothing, and there is no third source now that a row carries no `name`.\n return selected?.breadcrumb || selectedId;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { SparkRow, valueFor } from '@mintplayer/ng-spark/models';\n\n/**\n * The text a reference picker's result grid shows in one column.\n *\n * ⚠️ **Reads the row through {@link valueFor}, not through a shape it assumes.** This used to do\n * `item.attributes.find(...)`, which is the `PersistentObject` shape — but a picker's rows are\n * `QueryResultItem`s, which carry `values` (`[{ key, value, breadcrumb }]`) and have no `attributes`\n * at all. Every cell therefore threw `can't access property \"find\", item.attributes is undefined`,\n * and the picker rendered nothing selectable.\n *\n * There are three row shapes in this codebase and `valueFor` exists precisely so a consumer does not\n * have to know which one it was handed. Using it is also what keeps this working if a picker is ever\n * pointed at an AsDetail sub-table, whose rows are the flat record shape.\n */\n@Pipe({ name: 'referenceAttrValue', standalone: true, pure: true })\nexport class ReferenceAttrValuePipe implements PipeTransform {\n transform(item: SparkRow, attrName: string): any {\n const cell = valueFor(item, attrName);\n if (!cell) return '';\n // Breadcrumb first: a reference cell's value is an id, and the id is not what a person reads.\n if (cell.breadcrumb) return cell.breadcrumb;\n return cell.value ?? '';\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityType } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'referenceLinkRoute', standalone: true, pure: true })\nexport class ReferenceLinkRoutePipe implements PipeTransform {\n transform(referenceClrType: string, referenceId: any, allEntityTypes: EntityType[]): string[] | null {\n if (!referenceId || !referenceClrType) return null;\n const targetType = allEntityTypes.find(t => t.clrType === referenceClrType);\n if (!targetType) return null;\n\n // Presence in the catalogue means the caller may LIST this type; opening one is a different\n // right. Without this a type granted Query but not Read rendered a clickable reference whose\n // click refused on arrival — while the grid's own first column, three components away, gated on\n // canRead and got it right. Same question, two answers.\n if (targetType.canRead === false) return null;\n\n return ['/po', targetType.alias || targetType.id, referenceId];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { PersistentObject } from '@mintplayer/ng-spark/models';\n\nexport interface ReferenceChip {\n id: string;\n label: string;\n}\n\n/**\n * Resolves a multi-reference attribute (`dataType === 'Reference'`, `isArray === true`)\n * to a list of chips. The attribute's `value` carries the id array; `breadcrumbs` (a\n * server-resolved id → label map) provides each chip's display label, falling back to\n * the id when no breadcrumb is present. Mirrors the single-reference `breadcrumb`\n * display, applied per id.\n */\n@Pipe({ name: 'referenceChips', standalone: true, pure: true })\nexport class ReferenceChipsPipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null): ReferenceChip[] {\n const attr = item?.attributes.find(a => a.name === attrName);\n if (!attr || !Array.isArray(attr.value)) return [];\n const breadcrumbs = attr.breadcrumbs ?? {};\n return attr.value\n .filter(v => v != null && v !== '')\n .map(v => {\n const id = String(v);\n return { id, label: breadcrumbs[id] || id };\n });\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { ProgramUnit } from '@mintplayer/ng-spark/models';\n\n/**\n * Maps a program unit to the router commands that open it. Type comparisons are exact: the\n * server's loader canonicalizes `type` casing, so tolerating variants here would only mask a\n * server that stopped doing so.\n *\n * Returns null for a `url` unit — an external address is an `<a href>`, not a router link; the\n * component rendering the menu owns that branch.\n */\n@Pipe({ name: 'routerLink', standalone: true, pure: true })\nexport class RouterLinkPipe implements PipeTransform {\n transform(unit: ProgramUnit): string[] | null {\n if (unit.type === 'query') {\n return ['/query', unit.alias || unit.queryId!];\n } else if (unit.type === 'persistentObject') {\n const type = unit.alias || unit.persistentObjectId!;\n return unit.objectId ? ['/po', type, unit.objectId] : ['/po', type];\n } else if (unit.type === 'url') {\n return null;\n }\n return ['/'];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'asDetailType', standalone: true, pure: true })\nexport class AsDetailTypePipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, asDetailTypes: Record<string, EntityType>): EntityType | null {\n return asDetailTypes[attr.name] || null;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType } from '@mintplayer/ng-spark/models';\nimport { SparkAttributeRendererRegistration, isRowRendered } from '@mintplayer/ng-spark/renderers';\n\n/**\n * The columns of an AsDetail table: the row type's visible attributes, in order — minus those a\n * registered row renderer draws (`rowComponent`), when the host passes its renderer registry.\n */\n@Pipe({ name: 'asDetailColumns', standalone: true, pure: true })\nexport class AsDetailColumnsPipe implements PipeTransform {\n transform(\n attr: EntityAttributeDefinition,\n asDetailTypes: Record<string, EntityType>,\n renderers?: readonly SparkAttributeRendererRegistration[] | null,\n ): EntityAttributeDefinition[] {\n const type = asDetailTypes[attr.name];\n if (!type) return [];\n return type.attributes\n .filter(a => a.isVisible && !isRowRendered(a, renderers))\n .sort((a, b) => a.order - b.order);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType } from '@mintplayer/ng-spark/models';\nimport { SparkAttributeRendererRegistration, SparkResolvedRowRenderer, resolveRowRenderer } from '@mintplayer/ng-spark/renderers';\n\n/** The row renderer an AsDetail attribute's row type names (`rowComponent`), or null. */\n@Pipe({ name: 'asDetailRowRenderer', standalone: true, pure: true })\nexport class AsDetailRowRendererPipe implements PipeTransform {\n transform(\n attr: EntityAttributeDefinition,\n asDetailTypes: Record<string, EntityType>,\n renderers: readonly SparkAttributeRendererRegistration[] | null | undefined,\n ): SparkResolvedRowRenderer | null {\n return resolveRowRenderer(asDetailTypes[attr.name], renderers);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { AS_DETAIL_BREADCRUMBS_KEY, EntityAttributeDefinition, PersistentObject, QueryResultItem, selfBreadcrumb } from '@mintplayer/ng-spark/models';\n\n/** What a cell can resolve to: the column's own type, or `null` when it has no value. */\nexport type AsDetailCellValue = string | number | boolean | Date | null;\n\n/**\n * The value of one cell in an `AsDetail` table, **typed by the column's `dataType`** rather than\n * flattened to a string.\n *\n * <p>\n * This used to end in `return String(value)` for everything, and declare `: string`. That is fine\n * for a cell which is only interpolated, and wrong for one whose consumer inspects it —\n * `spark-grid-cell` branches on the column type and binds `[checked]=\"display() === true\"` and\n * `[indeterminate]=\"display() == null\"` for a boolean, so it needs the value and not a rendering\n * of it.\n * </p>\n * <p>\n * The visible symptom was a detail page showing unticked checkboxes for rows whose edit form showed\n * them ticked — the edit form reads `attribute.objects` directly, so it never went through here. All\n * three states of a `bool?` were affected, and one of them silently:\n * </p>\n * <ul>\n * <li><b>true</b> — `'true' === true` is false, so it rendered <b>unchecked</b>;</li>\n * <li><b>null</b> — became `''`, and `'' == null` is false, so it rendered unchecked <b>and\n * determinate</b>, i.e. claiming to be false;</li>\n * <li><b>false</b> — `'false'` is not `=== true`, so it rendered correctly <b>by accident</b>.</li>\n * </ul>\n * <p>\n * Types that stay strings do so deliberately, not by omission. `color`, `image` and `url` are bound\n * into a style, an `src` and an `href`, so a string is what they need. `Reference` and `AsDetail`\n * resolve to a breadcrumb, which is a label rather than a value.\n * </p>\n * <p>\n * `date` and `datetime` return a real `Date`. They arrive as ISO strings, and the cell used to\n * print that string verbatim — `2026-09-07T13:25:57.3119825Z` in a table column. Returning the\n * parsed value lets `spark-grid-cell` format it in one place for both grids; an unparseable value\n * falls back to its text, because showing what is there beats showing `Invalid Date`.\n * </p>\n */\n@Pipe({ name: 'asDetailCellValue', standalone: true, pure: true })\nexport class AsDetailCellValuePipe implements PipeTransform {\n transform(\n row: Record<string, any>,\n parentAttr: EntityAttributeDefinition,\n col: EntityAttributeDefinition,\n asDetailRefOptions: Record<string, Record<string, QueryResultItem[]>>,\n ): AsDetailCellValue {\n const value = row[col.name];\n\n // Before the null short-circuit, because for these types \"absent\" is a value the consumer has\n // to be able to see: `null` is what makes a checkbox indeterminate rather than false, and it is\n // what distinguishes \"no number\" from the number zero. `''` erases both.\n switch (col.dataType) {\n case 'boolean':\n return typeof value === 'boolean' ? value : null;\n\n case 'number':\n case 'decimal': {\n if (value == null || value === '') return null;\n const asNumber = typeof value === 'number' ? value : Number(value);\n // A value that is not a number is passed through as text rather than reported as NaN: the\n // cell's job is to show what is there, and \"NaN\" tells the reader nothing about it.\n return Number.isNaN(asNumber) ? String(value) : asNumber;\n }\n\n case 'date':\n case 'datetime': {\n if (value == null || value === '') return null;\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n const parsed = new Date(value as string);\n // Same rule as the number branch: an unparseable date shows its own text rather than\n // \"Invalid Date\", which would hide the value that is actually stored.\n return Number.isNaN(parsed.getTime()) ? String(value) : parsed;\n }\n }\n\n if (value == null) return '';\n\n if (col.dataType === 'Reference' && col.query) {\n // Prefer the breadcrumb the server already resolved by id — it is page-independent, so it\n // renders the label even when the referenced document falls outside the reference query's\n // first options page (issue #185).\n const serverBreadcrumb = (row[AS_DETAIL_BREADCRUMBS_KEY] as Record<string, string> | undefined)?.[col.name];\n if (serverBreadcrumb) return serverBreadcrumb;\n\n // Fallback: resolve against the loaded options page (legacy path).\n const parentOptions = asDetailRefOptions[parentAttr.name];\n if (parentOptions) {\n const options = parentOptions[col.name];\n if (options) {\n const match = options.find(o => o.id === value);\n if (match) return match.breadcrumb || String(value);\n }\n }\n }\n\n // A nested-AsDetail column's value is an inner dict, so String(value) produced \"[object\n // Object]\". The flattener kept that object's server-resolved breadcrumb for exactly this.\n if (col.dataType === 'AsDetail' && typeof value === 'object') {\n return selfBreadcrumb(value as Record<string, any>, col.asDetailType) ?? '';\n }\n\n return String(value);\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType, selfBreadcrumb } from '@mintplayer/ng-spark/models';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { applyFieldTemplate } from './apply-field-template';\n\n@Pipe({ name: 'asDetailDisplayValue', standalone: true, pure: true })\nexport class AsDetailDisplayValuePipe implements PipeTransform {\n private readonly lang = inject(SparkLanguageService);\n\n transform(attr: EntityAttributeDefinition, formData: Record<string, any>, asDetailTypes: Record<string, EntityType>): string {\n const value = formData[attr.name];\n if (!value) return this.lang.t('common.notSet');\n\n const asDetailType = asDetailTypes[attr.name] || null;\n\n // The server's own resolution wins, because it can render templates this side cannot: a\n // breadcrumb may name a property that `[IgnoreProperty]` deliberately keeps out of the model\n // (HR's `Address.Crumb`), and no amount of client-side substitution will ever find it. Passing\n // the type name filters out the mapper's \"template rendered blank\" placeholder, which is the\n // bare type name rather than an empty string — see `selfBreadcrumb`.\n const resolved = selfBreadcrumb(value, asDetailType?.name);\n if (resolved) return resolved;\n\n // Still correct for a template naming real, projected attributes (`\"{Street}, {City}\"`), which\n // is the common case and the only one reachable on the create path, where there is no server\n // object to have resolved anything yet.\n if (asDetailType?.breadcrumb) {\n const result = applyFieldTemplate(asDetailType.breadcrumb, value);\n if (result && result.trim()) return result;\n }\n\n return this.lang.t('common.clickToEdit');\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityPermissions } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'canCreateDetailRow', standalone: true, pure: true })\nexport class CanCreateDetailRowPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, permissions: Record<string, EntityPermissions>): boolean {\n const perms = permissions[attr.name];\n // Fail closed. An absent entry means the child type's permissions were never fetched, not that\n // the user may do this -- and since the save path now enforces `New/{RowType}`, defaulting\n // to true renders a button whose save is refused. The fetch is also not guaranteed: it used to\n // go through the entity-type catalogue, which is Query-gated, so a user with `New` but not\n // `Query` never got an entry at all.\n return perms ? perms.canCreate : false;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityPermissions } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'canDeleteDetailRow', standalone: true, pure: true })\nexport class CanDeleteDetailRowPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, permissions: Record<string, EntityPermissions>): boolean {\n const perms = permissions[attr.name];\n // Fail closed. An absent entry means the child type's permissions were never fetched, not that\n // the user may do this -- and since the save path now enforces `Delete/{RowType}`, defaulting\n // to true renders a button whose save is refused. The fetch is also not guaranteed: it used to\n // go through the entity-type catalogue, which is Query-gated, so a user with `Delete` but not\n // `Query` never got an entry at all.\n return perms ? perms.canDelete : false;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityPermissions } from '@mintplayer/ng-spark/models';\n\n/**\n * Whether the caller may edit rows of an AsDetail attribute's row type.\n *\n * The third of the `can*DetailRow` family, and the one the server was already answering. `canEdit`\n * has been computed, shipped and fetched per AsDetail attribute since row rights landed — it was\n * simply never read for the row path, so the form offered an edit the save would undo.\n *\n * ⚠️ Apply this **read-only, never hidden**. A row the caller may not edit must stay fully\n * readable; hiding the inputs would hide the data, which is a worse answer than showing it\n * uneditable.\n */\n@Pipe({ name: 'canEditDetailRow', standalone: true, pure: true })\nexport class CanEditDetailRowPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, permissions: Record<string, EntityPermissions>): boolean {\n const perms = permissions[attr.name];\n // Fail closed, for the same reason as its two siblings: an absent entry means the row type's\n // permissions were never fetched, not that the user may edit. And the save path enforces\n // `Edit/{RowType}` independently -- it silently restores the stored row and warns -- so\n // defaulting to true offers an edit whose result is discarded.\n return perms ? perms.canEdit : false;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { ELookupDisplayType, EntityAttributeDefinition, LookupReference } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'lookupDisplayType', standalone: true, pure: true })\nexport class LookupDisplayTypePipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, lookupRefOptions: Record<string, LookupReference>): ELookupDisplayType {\n const lookupRef = attr.lookupReferenceType ? lookupRefOptions[attr.lookupReferenceType] : null;\n return lookupRef?.displayType ?? ELookupDisplayType.Dropdown;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, LookupReference, resolveTranslation } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'lookupDisplayValue', standalone: true, pure: true })\nexport class LookupDisplayValuePipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, formData: Record<string, any>, lookupRefOptions: Record<string, LookupReference>): string {\n const currentValue = formData[attr.name];\n if (currentValue == null || currentValue === '') return '';\n\n const lookupRef = attr.lookupReferenceType ? lookupRefOptions[attr.lookupReferenceType] : null;\n const options = lookupRef?.values.filter(v => v.isActive) || [];\n const selected = options.find(o => o.key === String(currentValue));\n if (!selected) return String(currentValue);\n\n return resolveTranslation(selected.values) || selected.key;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, LookupReference, LookupReferenceValue } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'lookupOptions', standalone: true, pure: true })\nexport class LookupOptionsPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, lookupRefOptions: Record<string, LookupReference>): LookupReferenceValue[] {\n const lookupRef = attr.lookupReferenceType ? lookupRefOptions[attr.lookupReferenceType] : null;\n return lookupRef?.values.filter(v => v.isActive) || [];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, PersistentObject, QueryResultItem } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'inlineRefOptions', standalone: true, pure: true })\nexport class InlineRefOptionsPipe implements PipeTransform {\n transform(parentAttr: EntityAttributeDefinition, col: EntityAttributeDefinition, asDetailRefOptions: Record<string, Record<string, QueryResultItem[]>>): QueryResultItem[] {\n return asDetailRefOptions[parentAttr.name]?.[col.name] || [];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { ValidationError, resolveTranslation } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'errorForAttribute', standalone: true, pure: true })\nexport class ErrorForAttributePipe implements PipeTransform {\n transform(attrName: string, validationErrors: ValidationError[]): string | null {\n const error = validationErrors.find(e => e.attributeName === attrName);\n return error ? resolveTranslation(error.errorMessage) : null;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({ name: 'iconName', standalone: true, pure: true })\nexport class IconNamePipe implements PipeTransform {\n transform(value: string | undefined, fallback: string): string {\n const iconClass = value || fallback;\n // Strip 'bi-' prefix if present\n return iconClass.startsWith('bi-') ? iconClass.substring(3) : iconClass;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { PersistentObject, nestedPoToDisplayRow } from '@mintplayer/ng-spark/models';\n\n/**\n * Resolves an attribute to a list of flat row dicts for the detail-page table.\n * AsDetail array attributes carry their data as nested PersistentObjects in\n * <c>attr.objects</c>, not <c>attr.value</c> — the server stopped putting flat\n * dicts in <c>value</c> when AsDetail moved to its dedicated wire shape. Without\n * this branch, the detail page rendered AsDetail arrays as empty tables even\n * when the edit page (which reads <c>attr.objects</c> directly) showed rows.\n */\n@Pipe({ name: 'arrayValue', standalone: true, pure: true })\nexport class ArrayValuePipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null): Record<string, any>[] {\n const attr = item?.attributes.find(a => a.name === attrName);\n if (!attr) return [];\n if (attr.dataType === 'AsDetail' && attr.isArray && Array.isArray(attr.objects)) {\n return attr.objects.map(po => nestedPoToDisplayRow(po));\n }\n if (Array.isArray(attr.value)) return attr.value;\n return [];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport {\n LookupReference,\n QueryColumn,\n QueryResultItem,\n resolveTranslation,\n resolvedBreadcrumb,\n valueFor,\n} from '@mintplayer/ng-spark/models';\nimport { ReferenceChip } from './reference-chips.pipe';\n\n/**\n * The display text of one query-grid cell.\n *\n * The attribute-shaped sibling of this ({@link AttributeValuePipe}) still serves detail and edit\n * pages, which do work in attributes. They are deliberately not one pipe: a query row is a flat\n * projection with no nested objects and no per-row metadata, so the fallbacks that make sense\n * there — reaching into `attr.object`, recomputing a breadcrumb template client-side — are not\n * merely unnecessary here, they are unreachable. Sharing the code would mean carrying branches\n * that can never fire and inviting the next reader to feed one shape into the other.\n */\n@Pipe({ name: 'queryCellValue', standalone: true, pure: true })\nexport class QueryCellValuePipe implements PipeTransform {\n transform(\n column: QueryColumn,\n item: QueryResultItem | null,\n lookupRefOptions: Record<string, LookupReference>,\n ): any {\n const cell = valueFor(item, column.name);\n if (!cell) return '';\n\n // A resolved reference display wins: the server can resolve breadcrumb templates this side\n // cannot (a template may name a computed property `[IgnoreProperty]` keeps out of the model).\n // Except the CLR type name, which is the server's placeholder for a template that rendered\n // blank, not a label (#384). `asDetailType` is undefined on every other kind of column, so\n // nothing else is filtered.\n const resolved = resolvedBreadcrumb(cell.breadcrumb, column.asDetailType);\n if (resolved) return resolved;\n\n if (column.dataType === 'AsDetail') {\n // An array cell carries a child COUNT rather than the children, so the wording — and its\n // pluralisation — is decided here, where the language is. A single-child cell carries the\n // child object (for a renderer to read) and is displayed by the server-resolved breadcrumb\n // above, never by stringifying it here.\n if (!column.isArray) return '';\n const count = typeof cell.value === 'number' ? cell.value : 0;\n return count === 0 ? '' : `${count} item${count !== 1 ? 's' : ''}`;\n }\n\n if (column.lookupReferenceType && cell.value != null && cell.value !== '') {\n const lookupRef = lookupRefOptions[column.lookupReferenceType];\n const option = lookupRef?.values.find(v => v.key === String(cell.value));\n if (option) return resolveTranslation(option.values) || option.key;\n }\n\n // Null rather than '' so the cell can tell \"false\" from \"unset\" and render an indeterminate\n // checkbox instead of an unchecked one.\n if (column.dataType === 'boolean') return cell.value ?? null;\n\n // Dates are parsed here for the same reason booleans are passed through above: the cell needs\n // the value, not a rendering of it. Both grids feed `spark-grid-cell`, so both have to agree on\n // the type or the same `datetime` column formats one way in a query grid and another in an\n // AsDetail table. Unparseable input keeps its own text rather than becoming \"Invalid Date\".\n if (column.dataType === 'date' || column.dataType === 'datetime') {\n if (cell.value == null || cell.value === '') return null;\n if (cell.value instanceof Date) return Number.isNaN(cell.value.getTime()) ? null : cell.value;\n const parsed = new Date(cell.value as string);\n return Number.isNaN(parsed.getTime()) ? String(cell.value) : parsed;\n }\n\n return cell.value ?? '';\n }\n}\n\n/**\n * Resolves a multi-reference column (`dataType === 'Reference'`, `isArray === true`) to chips.\n *\n * The cell's `value` carries the id array and `breadcrumbs` the server-resolved id → label map,\n * falling back to the id itself when a label is missing — the same display rule as a single\n * reference, applied per id.\n */\n@Pipe({ name: 'queryReferenceChips', standalone: true, pure: true })\nexport class QueryReferenceChipsPipe implements PipeTransform {\n transform(column: QueryColumn, item: QueryResultItem | null): ReferenceChip[] {\n const cell = valueFor(item, column.name);\n if (!cell || !Array.isArray(cell.value)) return [];\n\n const breadcrumbs = cell.breadcrumbs ?? {};\n return cell.value\n .filter(v => v != null && v !== '')\n .map(v => {\n const id = String(v);\n return { id, label: breadcrumbs[id] || id };\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-pipes.mjs","names":[],"sources":["../../pipes/src/translate-key.pipe.ts","../../pipes/src/resolve-translation.pipe.ts","../../pipes/src/input-type.pipe.ts","../../pipes/src/parsed-date.pipe.ts","../../pipes/src/apply-field-template.ts","../../pipes/src/attribute-value.pipe.ts","../../pipes/src/raw-attribute-value.pipe.ts","../../pipes/src/reference-display-value.pipe.ts","../../pipes/src/reference-attr-value.pipe.ts","../../pipes/src/reference-link-route.pipe.ts","../../pipes/src/reference-chips.pipe.ts","../../pipes/src/router-link.pipe.ts","../../pipes/src/as-detail-type.pipe.ts","../../pipes/src/as-detail-columns.pipe.ts","../../pipes/src/as-detail-row-renderer.pipe.ts","../../pipes/src/as-detail-cell-value.pipe.ts","../../pipes/src/as-detail-display-value.pipe.ts","../../pipes/src/can-create-detail-row.pipe.ts","../../pipes/src/can-delete-detail-row.pipe.ts","../../pipes/src/can-edit-detail-row.pipe.ts","../../pipes/src/lookup-display-type.pipe.ts","../../pipes/src/lookup-display-value.pipe.ts","../../pipes/src/lookup-options.pipe.ts","../../pipes/src/inline-ref-options.pipe.ts","../../pipes/src/error-for-attribute.pipe.ts","../../pipes/src/icon-name.pipe.ts","../../pipes/src/array-value.pipe.ts","../../pipes/src/query-cell.pipe.ts"],"sourcesContent":["import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\n\n@Pipe({ name: 't', pure: false, standalone: true })\nexport class TranslateKeyPipe implements PipeTransform {\n private readonly lang = inject(SparkLanguageService);\n\n transform(key: string): string {\n return this.lang.t(key);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { TranslatedString, resolveTranslation } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'resolveTranslation', standalone: true, pure: false })\nexport class ResolveTranslationPipe implements PipeTransform {\n transform(value: TranslatedString | undefined, fallback?: string): string {\n return resolveTranslation(value) || fallback || '';\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({ name: 'inputType', standalone: true, pure: true })\nexport class InputTypePipe implements PipeTransform {\n transform(dataType: string): string {\n switch (dataType) {\n case 'number':\n case 'decimal':\n return 'number';\n case 'boolean':\n return 'checkbox';\n case 'datetime':\n return 'datetime-local';\n case 'date':\n return 'date';\n case 'color':\n return 'color';\n case 'url':\n case 'image':\n // Both hold a URL, so `url` gets the browser's own validation and keyboard. `image` is a\n // link to an image rather than an upload: the value is still a string the author types.\n return 'url';\n default:\n return 'text';\n }\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n/**\n * Parses a wire value into a `Date`, or `null` when it is absent or unparseable.\n *\n * Exists so the detail page and the grid agree. The grid gets its `Date` from `queryCellValue`, which\n * needs a cell and a column and so cannot be reused on a detail attribute; before this, the detail page\n * had no parsing step at all and printed the raw ISO string — `2026-12-31T23:59:00-08:00` in a\n * definition list, while the grid showed `01/01/2027, 08:59` for the same document.\n *\n * The parsing contract is deliberately identical to `queryCellValue`'s: `new Date(...)`, and an\n * unparseable value yields `null` so the caller can fall back to its own text rather than rendering\n * \"Invalid Date\".\n *\n * Note what `new Date(...)` does to an offset, because it is the whole reason this is a separate step:\n * it collapses the value to an instant and forgets the offset it was written with. That is correct for\n * display — a viewer wants the moment in their own zone — but it means a `Date` can never be the thing\n * you reach for when you need the *originating* wall clock. Read that from the wire value instead.\n */\n@Pipe({ name: 'parsedDate', standalone: true, pure: true })\nexport class ParsedDatePipe implements PipeTransform {\n transform(value: unknown): Date | null {\n if (value == null || value === '') return null;\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (typeof value !== 'string' && typeof value !== 'number') return null;\n\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n }\n}\n","/**\n * Fills a `{Field}` template from a plain object's own fields. Used ONLY for AsDetail\n * nested-object summary display: an embedded value object (no id, no document) is rendered\n * client-side from its own fields. This is deliberately distinct from entity/reference\n * breadcrumbs — those are resolved recursively on the server (BreadcrumbResolver) and the\n * client only reads the resulting strings. Unknown or null placeholders render empty.\n */\nexport function applyFieldTemplate(template: string, data: Record<string, any>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_match, propertyName) => {\n const value = data[propertyName];\n return value != null ? String(value) : '';\n });\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType, LookupReference, PersistentObject, isReservedAsDetailKey, nestedPoToDict, resolveTranslation, resolvedBreadcrumb } from '@mintplayer/ng-spark/models';\nimport { applyFieldTemplate } from './apply-field-template';\n\n@Pipe({ name: 'attributeValue', standalone: true, pure: true })\nexport class AttributeValuePipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null, entityType: EntityType | null, lookupRefOptions: Record<string, LookupReference>, allEntityTypes: EntityType[]): any {\n if (!item) return '';\n const attr = item.attributes.find(a => a.name === attrName);\n if (!attr) return '';\n\n if (attr.breadcrumb) return attr.breadcrumb;\n\n const attrDef = entityType?.attributes.find(a => a.name === attrName);\n if (attrDef?.dataType === 'AsDetail') {\n // Server emits nested PO(s) in attr.objects (array) / attr.object (single) — attr.value is null.\n if (attr.isArray) {\n const count = attr.objects?.length ?? 0;\n if (count === 0) return '';\n return `${count} item${count !== 1 ? 's' : ''}`;\n }\n if (attr.object) {\n // The server has already resolved this object's breadcrumb, and it can resolve templates\n // this side cannot. A type's breadcrumb may name a computed property that `[IgnoreProperty]`\n // keeps out of the model — HR's `Address.Crumb` is exactly that, `[Breadcrumb, IgnoreProperty]`\n // — so the template `{Crumb}` has no matching attribute here and never will. Recomputing it\n // client-side produced an empty string and fell through to \"(object)\" while the correct\n // \"Voorbeeldstraat 1, 1000 Brussel\" sat unread on `attr.object.breadcrumb`.\n //\n // One value on that property is NOT a breadcrumb: when the template renders blank the\n // server substitutes the CLR type name, and printing it reads as data — a `Build` whose\n // `Feedback.State` was unset showed the literal \"BuildFeedback\" (#384). Filtering it here\n // lets the fallbacks below run, which is what they are for.\n const resolved = resolvedBreadcrumb(attr.object.breadcrumb, attrDef.asDetailType);\n if (resolved) return resolved;\n return this.formatAsDetailValue(attrDef, nestedPoToDict(attr.object), allEntityTypes);\n }\n }\n\n if (attrDef?.lookupReferenceType && attr.value != null && attr.value !== '') {\n const lookupRef = lookupRefOptions[attrDef.lookupReferenceType];\n if (lookupRef) {\n const option = lookupRef.values.find(v => v.key === String(attr.value));\n if (option) {\n return resolveTranslation(option.values) || option.key;\n }\n }\n }\n\n if (attrDef?.dataType === 'boolean') {\n return attr.value ?? null;\n }\n\n return attr.value ?? '';\n }\n\n private formatAsDetailValue(attrDef: EntityAttributeDefinition, value: Record<string, any>, allEntityTypes: EntityType[]): string {\n const asDetailType = allEntityTypes.find(t => t.clrType === attrDef.asDetailType);\n\n if (asDetailType?.breadcrumb) {\n const result = applyFieldTemplate(asDetailType.breadcrumb, value);\n if (result && result.trim()) return result;\n }\n\n // Last resort, reached when the type declares no breadcrumb or its template resolved to\n // nothing. Joining the scalar values is always more use to a reader than \"(object)\", which\n // named the failure rather than the row and looked identical for every unresolvable cell.\n // Reserved keys are skipped: `nestedPoToDict` stashes the row key and the row's own breadcrumb\n // in this same dict, and joining those prints a guid and a placeholder as though they were\n // fields. Previously masked -- this branch was only reachable when the server sent no\n // breadcrumb, which is exactly when there was nothing stashed to leak.\n return Object.entries(value)\n .filter(([k, v]) => !isReservedAsDetailKey(k) && v != null && typeof v !== 'object' && String(v).trim() !== '')\n .map(([, v]) => v)\n .join(', ');\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { PersistentObject } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'rawAttributeValue', standalone: true, pure: true })\nexport class RawAttributeValuePipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null): any {\n return item?.attributes.find(a => a.name === attrName)?.value;\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { EntityAttributeDefinition, PersistentObject, QueryResultItem } from '@mintplayer/ng-spark/models';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\n\n@Pipe({ name: 'referenceDisplayValue', standalone: true, pure: true })\nexport class ReferenceDisplayValuePipe implements PipeTransform {\n private readonly lang = inject(SparkLanguageService);\n\n transform(attr: EntityAttributeDefinition, formData: Record<string, any>, referenceOptions: Record<string, QueryResultItem[]>): string {\n const selectedId = formData[attr.name];\n if (!selectedId) return this.lang.t('common.notSelected');\n\n const options = referenceOptions[attr.name] || [];\n const selected = options.find(o => o.id === selectedId);\n // The server resolves a row's display string into `breadcrumb`; falling back to the id\n // beats showing nothing, and there is no third source now that a row carries no `name`.\n return selected?.breadcrumb || selectedId;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { SparkRow, valueFor } from '@mintplayer/ng-spark/models';\n\n/**\n * The text a reference picker's result grid shows in one column.\n *\n * ⚠️ **Reads the row through {@link valueFor}, not through a shape it assumes.** This used to do\n * `item.attributes.find(...)`, which is the `PersistentObject` shape — but a picker's rows are\n * `QueryResultItem`s, which carry `values` (`[{ key, value, breadcrumb }]`) and have no `attributes`\n * at all. Every cell therefore threw `can't access property \"find\", item.attributes is undefined`,\n * and the picker rendered nothing selectable.\n *\n * There are three row shapes in this codebase and `valueFor` exists precisely so a consumer does not\n * have to know which one it was handed. Using it is also what keeps this working if a picker is ever\n * pointed at an AsDetail sub-table, whose rows are the flat record shape.\n */\n@Pipe({ name: 'referenceAttrValue', standalone: true, pure: true })\nexport class ReferenceAttrValuePipe implements PipeTransform {\n transform(item: SparkRow, attrName: string): any {\n const cell = valueFor(item, attrName);\n if (!cell) return '';\n // Breadcrumb first: a reference cell's value is an id, and the id is not what a person reads.\n if (cell.breadcrumb) return cell.breadcrumb;\n return cell.value ?? '';\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityType } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'referenceLinkRoute', standalone: true, pure: true })\nexport class ReferenceLinkRoutePipe implements PipeTransform {\n transform(referenceClrType: string, referenceId: any, allEntityTypes: EntityType[]): string[] | null {\n if (!referenceId || !referenceClrType) return null;\n const targetType = allEntityTypes.find(t => t.clrType === referenceClrType);\n if (!targetType) return null;\n\n // Presence in the catalogue means the caller may LIST this type; opening one is a different\n // right. Without this a type granted Query but not Read rendered a clickable reference whose\n // click refused on arrival — while the grid's own first column, three components away, gated on\n // canRead and got it right. Same question, two answers.\n if (targetType.canRead === false) return null;\n\n return ['/po', targetType.alias || targetType.id, referenceId];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { PersistentObject } from '@mintplayer/ng-spark/models';\n\nexport interface ReferenceChip {\n id: string;\n label: string;\n}\n\n/**\n * Resolves a multi-reference attribute (`dataType === 'Reference'`, `isArray === true`)\n * to a list of chips. The attribute's `value` carries the id array; `breadcrumbs` (a\n * server-resolved id → label map) provides each chip's display label, falling back to\n * the id when no breadcrumb is present. Mirrors the single-reference `breadcrumb`\n * display, applied per id.\n */\n@Pipe({ name: 'referenceChips', standalone: true, pure: true })\nexport class ReferenceChipsPipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null): ReferenceChip[] {\n const attr = item?.attributes.find(a => a.name === attrName);\n if (!attr || !Array.isArray(attr.value)) return [];\n const breadcrumbs = attr.breadcrumbs ?? {};\n return attr.value\n .filter(v => v != null && v !== '')\n .map(v => {\n const id = String(v);\n return { id, label: breadcrumbs[id] || id };\n });\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { ProgramUnit } from '@mintplayer/ng-spark/models';\n\n/**\n * Maps a program unit to the router commands that open it. Type comparisons are exact: the\n * server's loader canonicalizes `type` casing, so tolerating variants here would only mask a\n * server that stopped doing so.\n *\n * Returns null for a `url` unit — an external address is an `<a href>`, not a router link; the\n * component rendering the menu owns that branch.\n */\n@Pipe({ name: 'routerLink', standalone: true, pure: true })\nexport class RouterLinkPipe implements PipeTransform {\n transform(unit: ProgramUnit): string[] | null {\n if (unit.type === 'query') {\n return ['/query', unit.alias || unit.queryId!];\n } else if (unit.type === 'persistentObject') {\n const type = unit.alias || unit.persistentObjectId!;\n return unit.objectId ? ['/po', type, unit.objectId] : ['/po', type];\n } else if (unit.type === 'url') {\n return null;\n }\n return ['/'];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'asDetailType', standalone: true, pure: true })\nexport class AsDetailTypePipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, asDetailTypes: Record<string, EntityType>): EntityType | null {\n return asDetailTypes[attr.name] || null;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType, ShowedOn, hasShowedOnFlag } from '@mintplayer/ng-spark/models';\nimport { SparkAttributeRendererRegistration, isRowRendered } from '@mintplayer/ng-spark/renderers';\n\n/**\n * The columns of an AsDetail table: the row type's visible attributes, in order — minus those a\n * registered row renderer draws (`rowComponent`), when the host passes its renderer registry.\n *\n * An attribute the row type's model draws nowhere (`showedOn: None`, #264) is not a column. Columns are per\n * type, so they come from the model; a runtime `showedOn` on one row cannot add or remove a column.\n */\n@Pipe({ name: 'asDetailColumns', standalone: true, pure: true })\nexport class AsDetailColumnsPipe implements PipeTransform {\n transform(\n attr: EntityAttributeDefinition,\n asDetailTypes: Record<string, EntityType>,\n renderers?: readonly SparkAttributeRendererRegistration[] | null,\n ): EntityAttributeDefinition[] {\n const type = asDetailTypes[attr.name];\n if (!type) return [];\n return type.attributes\n .filter(a => isDrawnSomewhere(a) && !isRowRendered(a, renderers))\n .sort((a, b) => a.order - b.order);\n }\n}\n\nfunction isDrawnSomewhere(attr: EntityAttributeDefinition): boolean {\n return hasShowedOnFlag(attr.showedOn, ShowedOn.Query) || hasShowedOnFlag(attr.showedOn, ShowedOn.PersistentObject);\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType } from '@mintplayer/ng-spark/models';\nimport { SparkAttributeRendererRegistration, SparkResolvedRowRenderer, resolveRowRenderer } from '@mintplayer/ng-spark/renderers';\n\n/** The row renderer an AsDetail attribute's row type names (`rowComponent`), or null. */\n@Pipe({ name: 'asDetailRowRenderer', standalone: true, pure: true })\nexport class AsDetailRowRendererPipe implements PipeTransform {\n transform(\n attr: EntityAttributeDefinition,\n asDetailTypes: Record<string, EntityType>,\n renderers: readonly SparkAttributeRendererRegistration[] | null | undefined,\n ): SparkResolvedRowRenderer | null {\n return resolveRowRenderer(asDetailTypes[attr.name], renderers);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { AS_DETAIL_BREADCRUMBS_KEY, EntityAttributeDefinition, PersistentObject, QueryResultItem, selfBreadcrumb } from '@mintplayer/ng-spark/models';\n\n/** What a cell can resolve to: the column's own type, or `null` when it has no value. */\nexport type AsDetailCellValue = string | number | boolean | Date | null;\n\n/**\n * The value of one cell in an `AsDetail` table, **typed by the column's `dataType`** rather than\n * flattened to a string.\n *\n * <p>\n * This used to end in `return String(value)` for everything, and declare `: string`. That is fine\n * for a cell which is only interpolated, and wrong for one whose consumer inspects it —\n * `spark-grid-cell` branches on the column type and binds `[checked]=\"display() === true\"` and\n * `[indeterminate]=\"display() == null\"` for a boolean, so it needs the value and not a rendering\n * of it.\n * </p>\n * <p>\n * The visible symptom was a detail page showing unticked checkboxes for rows whose edit form showed\n * them ticked — the edit form reads `attribute.objects` directly, so it never went through here. All\n * three states of a `bool?` were affected, and one of them silently:\n * </p>\n * <ul>\n * <li><b>true</b> — `'true' === true` is false, so it rendered <b>unchecked</b>;</li>\n * <li><b>null</b> — became `''`, and `'' == null` is false, so it rendered unchecked <b>and\n * determinate</b>, i.e. claiming to be false;</li>\n * <li><b>false</b> — `'false'` is not `=== true`, so it rendered correctly <b>by accident</b>.</li>\n * </ul>\n * <p>\n * Types that stay strings do so deliberately, not by omission. `color`, `image` and `url` are bound\n * into a style, an `src` and an `href`, so a string is what they need. `Reference` and `AsDetail`\n * resolve to a breadcrumb, which is a label rather than a value.\n * </p>\n * <p>\n * `date` and `datetime` return a real `Date`. They arrive as ISO strings, and the cell used to\n * print that string verbatim — `2026-09-07T13:25:57.3119825Z` in a table column. Returning the\n * parsed value lets `spark-grid-cell` format it in one place for both grids; an unparseable value\n * falls back to its text, because showing what is there beats showing `Invalid Date`.\n * </p>\n */\n@Pipe({ name: 'asDetailCellValue', standalone: true, pure: true })\nexport class AsDetailCellValuePipe implements PipeTransform {\n transform(\n row: Record<string, any>,\n parentAttr: EntityAttributeDefinition,\n col: EntityAttributeDefinition,\n asDetailRefOptions: Record<string, Record<string, QueryResultItem[]>>,\n ): AsDetailCellValue {\n const value = row[col.name];\n\n // Before the null short-circuit, because for these types \"absent\" is a value the consumer has\n // to be able to see: `null` is what makes a checkbox indeterminate rather than false, and it is\n // what distinguishes \"no number\" from the number zero. `''` erases both.\n switch (col.dataType) {\n case 'boolean':\n return typeof value === 'boolean' ? value : null;\n\n case 'number':\n case 'decimal': {\n if (value == null || value === '') return null;\n const asNumber = typeof value === 'number' ? value : Number(value);\n // A value that is not a number is passed through as text rather than reported as NaN: the\n // cell's job is to show what is there, and \"NaN\" tells the reader nothing about it.\n return Number.isNaN(asNumber) ? String(value) : asNumber;\n }\n\n case 'date':\n case 'datetime': {\n if (value == null || value === '') return null;\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n const parsed = new Date(value as string);\n // Same rule as the number branch: an unparseable date shows its own text rather than\n // \"Invalid Date\", which would hide the value that is actually stored.\n return Number.isNaN(parsed.getTime()) ? String(value) : parsed;\n }\n }\n\n if (value == null) return '';\n\n if (col.dataType === 'Reference' && col.query) {\n // Prefer the breadcrumb the server already resolved by id — it is page-independent, so it\n // renders the label even when the referenced document falls outside the reference query's\n // first options page (issue #185).\n const serverBreadcrumb = (row[AS_DETAIL_BREADCRUMBS_KEY] as Record<string, string> | undefined)?.[col.name];\n if (serverBreadcrumb) return serverBreadcrumb;\n\n // Fallback: resolve against the loaded options page (legacy path).\n const parentOptions = asDetailRefOptions[parentAttr.name];\n if (parentOptions) {\n const options = parentOptions[col.name];\n if (options) {\n const match = options.find(o => o.id === value);\n if (match) return match.breadcrumb || String(value);\n }\n }\n }\n\n // A nested-AsDetail column's value is an inner dict, so String(value) produced \"[object\n // Object]\". The flattener kept that object's server-resolved breadcrumb for exactly this.\n if (col.dataType === 'AsDetail' && typeof value === 'object') {\n return selfBreadcrumb(value as Record<string, any>, col.asDetailType) ?? '';\n }\n\n return String(value);\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType, selfBreadcrumb } from '@mintplayer/ng-spark/models';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { applyFieldTemplate } from './apply-field-template';\n\n@Pipe({ name: 'asDetailDisplayValue', standalone: true, pure: true })\nexport class AsDetailDisplayValuePipe implements PipeTransform {\n private readonly lang = inject(SparkLanguageService);\n\n transform(attr: EntityAttributeDefinition, formData: Record<string, any>, asDetailTypes: Record<string, EntityType>): string {\n const value = formData[attr.name];\n if (!value) return this.lang.t('common.notSet');\n\n const asDetailType = asDetailTypes[attr.name] || null;\n\n // The server's own resolution wins, because it can render templates this side cannot: a\n // breadcrumb may name a property that `[IgnoreProperty]` deliberately keeps out of the model\n // (HR's `Address.Crumb`), and no amount of client-side substitution will ever find it. Passing\n // the type name filters out the mapper's \"template rendered blank\" placeholder, which is the\n // bare type name rather than an empty string — see `selfBreadcrumb`.\n const resolved = selfBreadcrumb(value, asDetailType?.name);\n if (resolved) return resolved;\n\n // Still correct for a template naming real, projected attributes (`\"{Street}, {City}\"`), which\n // is the common case and the only one reachable on the create path, where there is no server\n // object to have resolved anything yet.\n if (asDetailType?.breadcrumb) {\n const result = applyFieldTemplate(asDetailType.breadcrumb, value);\n if (result && result.trim()) return result;\n }\n\n return this.lang.t('common.clickToEdit');\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityPermissions } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'canCreateDetailRow', standalone: true, pure: true })\nexport class CanCreateDetailRowPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, permissions: Record<string, EntityPermissions>): boolean {\n const perms = permissions[attr.name];\n // Fail closed. An absent entry means the child type's permissions were never fetched, not that\n // the user may do this -- and since the save path now enforces `New/{RowType}`, defaulting\n // to true renders a button whose save is refused. The fetch is also not guaranteed: it used to\n // go through the entity-type catalogue, which is Query-gated, so a user with `New` but not\n // `Query` never got an entry at all.\n return perms ? perms.canCreate : false;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityPermissions } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'canDeleteDetailRow', standalone: true, pure: true })\nexport class CanDeleteDetailRowPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, permissions: Record<string, EntityPermissions>): boolean {\n const perms = permissions[attr.name];\n // Fail closed. An absent entry means the child type's permissions were never fetched, not that\n // the user may do this -- and since the save path now enforces `Delete/{RowType}`, defaulting\n // to true renders a button whose save is refused. The fetch is also not guaranteed: it used to\n // go through the entity-type catalogue, which is Query-gated, so a user with `Delete` but not\n // `Query` never got an entry at all.\n return perms ? perms.canDelete : false;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, EntityPermissions } from '@mintplayer/ng-spark/models';\n\n/**\n * Whether the caller may edit rows of an AsDetail attribute's row type.\n *\n * The third of the `can*DetailRow` family, and the one the server was already answering. `canEdit`\n * has been computed, shipped and fetched per AsDetail attribute since row rights landed — it was\n * simply never read for the row path, so the form offered an edit the save would undo.\n *\n * ⚠️ Apply this **read-only, never hidden**. A row the caller may not edit must stay fully\n * readable; hiding the inputs would hide the data, which is a worse answer than showing it\n * uneditable.\n */\n@Pipe({ name: 'canEditDetailRow', standalone: true, pure: true })\nexport class CanEditDetailRowPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, permissions: Record<string, EntityPermissions>): boolean {\n const perms = permissions[attr.name];\n // Fail closed, for the same reason as its two siblings: an absent entry means the row type's\n // permissions were never fetched, not that the user may edit. And the save path enforces\n // `Edit/{RowType}` independently -- it silently restores the stored row and warns -- so\n // defaulting to true offers an edit whose result is discarded.\n return perms ? perms.canEdit : false;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { ELookupDisplayType, EntityAttributeDefinition, LookupReference } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'lookupDisplayType', standalone: true, pure: true })\nexport class LookupDisplayTypePipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, lookupRefOptions: Record<string, LookupReference>): ELookupDisplayType {\n const lookupRef = attr.lookupReferenceType ? lookupRefOptions[attr.lookupReferenceType] : null;\n return lookupRef?.displayType ?? ELookupDisplayType.Dropdown;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, LookupReference, resolveTranslation } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'lookupDisplayValue', standalone: true, pure: true })\nexport class LookupDisplayValuePipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, formData: Record<string, any>, lookupRefOptions: Record<string, LookupReference>): string {\n const currentValue = formData[attr.name];\n if (currentValue == null || currentValue === '') return '';\n\n const lookupRef = attr.lookupReferenceType ? lookupRefOptions[attr.lookupReferenceType] : null;\n const options = lookupRef?.values.filter(v => v.isActive) || [];\n const selected = options.find(o => o.key === String(currentValue));\n if (!selected) return String(currentValue);\n\n return resolveTranslation(selected.values) || selected.key;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, LookupReference, LookupReferenceValue } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'lookupOptions', standalone: true, pure: true })\nexport class LookupOptionsPipe implements PipeTransform {\n transform(attr: EntityAttributeDefinition, lookupRefOptions: Record<string, LookupReference>): LookupReferenceValue[] {\n const lookupRef = attr.lookupReferenceType ? lookupRefOptions[attr.lookupReferenceType] : null;\n return lookupRef?.values.filter(v => v.isActive) || [];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EntityAttributeDefinition, PersistentObject, QueryResultItem } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'inlineRefOptions', standalone: true, pure: true })\nexport class InlineRefOptionsPipe implements PipeTransform {\n transform(parentAttr: EntityAttributeDefinition, col: EntityAttributeDefinition, asDetailRefOptions: Record<string, Record<string, QueryResultItem[]>>): QueryResultItem[] {\n return asDetailRefOptions[parentAttr.name]?.[col.name] || [];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { ValidationError, resolveTranslation } from '@mintplayer/ng-spark/models';\n\n@Pipe({ name: 'errorForAttribute', standalone: true, pure: true })\nexport class ErrorForAttributePipe implements PipeTransform {\n transform(attrName: string, validationErrors: ValidationError[]): string | null {\n const error = validationErrors.find(e => e.attributeName === attrName);\n return error ? resolveTranslation(error.errorMessage) : null;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({ name: 'iconName', standalone: true, pure: true })\nexport class IconNamePipe implements PipeTransform {\n transform(value: string | undefined, fallback: string): string {\n const iconClass = value || fallback;\n // Strip 'bi-' prefix if present\n return iconClass.startsWith('bi-') ? iconClass.substring(3) : iconClass;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { PersistentObject, nestedPoToDisplayRow } from '@mintplayer/ng-spark/models';\n\n/**\n * Resolves an attribute to a list of flat row dicts for the detail-page table.\n * AsDetail array attributes carry their data as nested PersistentObjects in\n * <c>attr.objects</c>, not <c>attr.value</c> — the server stopped putting flat\n * dicts in <c>value</c> when AsDetail moved to its dedicated wire shape. Without\n * this branch, the detail page rendered AsDetail arrays as empty tables even\n * when the edit page (which reads <c>attr.objects</c> directly) showed rows.\n */\n@Pipe({ name: 'arrayValue', standalone: true, pure: true })\nexport class ArrayValuePipe implements PipeTransform {\n transform(attrName: string, item: PersistentObject | null): Record<string, any>[] {\n const attr = item?.attributes.find(a => a.name === attrName);\n if (!attr) return [];\n if (attr.dataType === 'AsDetail' && attr.isArray && Array.isArray(attr.objects)) {\n return attr.objects.map(po => nestedPoToDisplayRow(po));\n }\n if (Array.isArray(attr.value)) return attr.value;\n return [];\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport {\n LookupReference,\n QueryColumn,\n QueryResultItem,\n resolveTranslation,\n resolvedBreadcrumb,\n valueFor,\n} from '@mintplayer/ng-spark/models';\nimport { ReferenceChip } from './reference-chips.pipe';\n\n/**\n * The display text of one query-grid cell.\n *\n * The attribute-shaped sibling of this ({@link AttributeValuePipe}) still serves detail and edit\n * pages, which do work in attributes. They are deliberately not one pipe: a query row is a flat\n * projection with no nested objects and no per-row metadata, so the fallbacks that make sense\n * there — reaching into `attr.object`, recomputing a breadcrumb template client-side — are not\n * merely unnecessary here, they are unreachable. Sharing the code would mean carrying branches\n * that can never fire and inviting the next reader to feed one shape into the other.\n */\n@Pipe({ name: 'queryCellValue', standalone: true, pure: true })\nexport class QueryCellValuePipe implements PipeTransform {\n transform(\n column: QueryColumn,\n item: QueryResultItem | null,\n lookupRefOptions: Record<string, LookupReference>,\n ): any {\n const cell = valueFor(item, column.name);\n if (!cell) return '';\n\n // A resolved reference display wins: the server can resolve breadcrumb templates this side\n // cannot (a template may name a computed property `[IgnoreProperty]` keeps out of the model).\n // Except the CLR type name, which is the server's placeholder for a template that rendered\n // blank, not a label (#384). `asDetailType` is undefined on every other kind of column, so\n // nothing else is filtered.\n const resolved = resolvedBreadcrumb(cell.breadcrumb, column.asDetailType);\n if (resolved) return resolved;\n\n if (column.dataType === 'AsDetail') {\n // An array cell carries a child COUNT rather than the children, so the wording — and its\n // pluralisation — is decided here, where the language is. A single-child cell carries the\n // child object (for a renderer to read) and is displayed by the server-resolved breadcrumb\n // above, never by stringifying it here.\n if (!column.isArray) return '';\n const count = typeof cell.value === 'number' ? cell.value : 0;\n return count === 0 ? '' : `${count} item${count !== 1 ? 's' : ''}`;\n }\n\n if (column.lookupReferenceType && cell.value != null && cell.value !== '') {\n const lookupRef = lookupRefOptions[column.lookupReferenceType];\n const option = lookupRef?.values.find(v => v.key === String(cell.value));\n if (option) return resolveTranslation(option.values) || option.key;\n }\n\n // Null rather than '' so the cell can tell \"false\" from \"unset\" and render an indeterminate\n // checkbox instead of an unchecked one.\n if (column.dataType === 'boolean') return cell.value ?? null;\n\n // Dates are parsed here for the same reason booleans are passed through above: the cell needs\n // the value, not a rendering of it. Both grids feed `spark-grid-cell`, so both have to agree on\n // the type or the same `datetime` column formats one way in a query grid and another in an\n // AsDetail table. Unparseable input keeps its own text rather than becoming \"Invalid Date\".\n if (column.dataType === 'date' || column.dataType === 'datetime') {\n if (cell.value == null || cell.value === '') return null;\n if (cell.value instanceof Date) return Number.isNaN(cell.value.getTime()) ? null : cell.value;\n const parsed = new Date(cell.value as string);\n return Number.isNaN(parsed.getTime()) ? String(cell.value) : parsed;\n }\n\n return cell.value ?? '';\n }\n}\n\n/**\n * Resolves a multi-reference column (`dataType === 'Reference'`, `isArray === true`) to chips.\n *\n * The cell's `value` carries the id array and `breadcrumbs` the server-resolved id → label map,\n * falling back to the id itself when a label is missing — the same display rule as a single\n * reference, applied per id.\n */\n@Pipe({ name: 'queryReferenceChips', standalone: true, pure: true })\nexport class QueryReferenceChipsPipe implements PipeTransform {\n transform(column: QueryColumn, item: QueryResultItem | null): ReferenceChip[] {\n const cell = valueFor(item, column.name);\n if (!cell || !Array.isArray(cell.value)) return [];\n\n const breadcrumbs = cell.breadcrumbs ?? {};\n return cell.value\n .filter(v => v != null && v !== '')\n .map(v => {\n const id = String(v);\n return { id, label: breadcrumbs[id] || id };\n });\n }\n}\n"],"mappings":"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"}
|