|
75 | 75 | #end |
76 | 76 | </div> |
77 | 77 |
|
| 78 | +#if($documentItemsLayout == "chat") |
| 79 | + <!-- Conversation thread (chat items) - the personal document renders the SAME chat the power |
| 80 | + document does (bubbles + composer through the personal items controller), never the generic |
| 81 | + line-items table. Own vs other alignment keys on the audit CreatedBy vs the logged-in user; |
| 82 | + an internal memo is tinted distinctly. Composed from shipped Harmonia primitives (inline |
| 83 | + styles + theme vars - see the power document view for why); the x-h-chat component family |
| 84 | + will own this styling once it ships. --> |
| 85 | + <div class="vbox gap-2" style="max-width: 900px" x-show="itemsEnabled"> |
| 86 | + <div x-h-toolbar data-variant="transparent" data-size="sm"> |
| 87 | + <span x-h-toolbar-title class="shrink-0" x-text="itemsDef ? T(itemsDef.tkey, '${documentItemsLabel}') : '${documentItemsLabel}'"></span> |
| 88 | + </div> |
| 89 | + <div x-show="itemsError" x-h-alert data-variant="negative" role="alert"><div x-h-alert-description x-text="itemsError"></div></div> |
| 90 | + <div class="vbox gap-3 w-full rounded-control border border-input bg-input-inner p-4" style="max-height: 28rem; overflow-y: auto" role="log" aria-live="polite"> |
| 91 | + <template x-for="(row, idx) in items" :key="row[itemsDef.primaryKey] != null ? row[itemsDef.primaryKey] : idx"> |
| 92 | + <div class="vbox gap-1" style="max-width: 85%" |
| 93 | + :style="row.CreatedBy === $store.currentUser.name ? 'align-self:flex-end;align-items:flex-end' : 'align-self:flex-start;align-items:flex-start'"> |
| 94 | + <div class="hbox items-baseline gap-2"> |
| 95 | + <span class="text-foreground text-xs font-medium" x-text="row.CreatedBy || T('$projectName:${tprefix}.defaults.system', 'System')"></span> |
| 96 | + <span class="text-muted-foreground text-xs" x-text="window.HarmoniaFormat.value(row.CreatedAt, true)"></span> |
| 97 | +#if($chatInternalProperty) |
| 98 | + <span x-show="row.${chatInternalProperty}" x-h-badge data-variant="warning" x-text="T('$projectName:${tprefix}.defaults.internal', 'Internal')"></span> |
| 99 | +#end |
| 100 | + </div> |
| 101 | + <div class="text-sm" style="border-radius: 0.5rem; padding: 0.5rem 0.75rem; white-space: pre-wrap; overflow-wrap: anywhere" |
| 102 | + :style="#if($chatInternalProperty)row.${chatInternalProperty} ? 'background:var(--muted);color:var(--foreground)' : (#end(row.CreatedBy === $store.currentUser.name ? 'background:var(--primary);color:var(--primary-foreground)' : 'background:var(--secondary);color:var(--secondary-foreground)')#if($chatInternalProperty))#end" |
| 103 | + x-text="row.${chatBodyProperty}"></div> |
| 104 | + </div> |
| 105 | + </template> |
| 106 | + <div x-show="items.length === 0" x-h-text.muted class="p-2" x-text="T('$projectName:${tprefix}.messages.noData', 'No messages yet.')"></div> |
| 107 | + </div> |
| 108 | + <div class="vbox gap-2"> |
| 109 | +#if($chatInternalProperty) |
| 110 | + <label class="hbox items-center gap-2 text-sm"> |
| 111 | + <span x-h-switch data-size="sm"><input type="checkbox" x-model="chatInternal" /></span> |
| 112 | + <span x-text="T('$projectName:${tprefix}.defaults.internalNote', 'Internal note (not visible to the requester)')"></span> |
| 113 | + </label> |
| 114 | +#end |
| 115 | + <div class="hbox items-end gap-2 w-full"> |
| 116 | + <textarea x-h-textarea rows="1" class="grow" x-model="chatDraft" @keydown.enter.prevent="sendMessage(chatDraft)" :disabled="chatSending" :aria-label="T('$projectName:${tprefix}.defaults.writeMessage', 'Write a message')" :placeholder="T('$projectName:${tprefix}.defaults.writeMessage', 'Write a message...')"></textarea> |
| 117 | + <button type="button" x-h-button data-variant="primary" @click="sendMessage(chatDraft)" :disabled="chatSending || !chatDraft.trim()" :aria-label="T('$projectName:${tprefix}.defaults.send', 'Send')"><i role="img" x-h-lucide data-lucide="send"></i></button> |
| 118 | + </div> |
| 119 | + </div> |
| 120 | + </div> |
| 121 | +#else |
78 | 122 | <!-- Line items (only once the document exists). Add/edit via a dialog; delete inline. --> |
79 | 123 | <div class="vbox gap-2" style="max-width: 900px" x-show="itemsEnabled"> |
80 | 124 | <div x-h-toolbar data-variant="transparent" data-size="sm"> |
|
114 | 158 | </table> |
115 | 159 | </div> |
116 | 160 | </div> |
| 161 | +#end |
117 | 162 |
|
118 | | - <!-- Totals footer (aggregate fields, read-only). --> |
| 163 | + <!-- Totals footer (aggregate fields, read-only). A SENSITIVE aggregate is omitted entirely: the |
| 164 | + personal controller nulls it on the wire, so rendering the row would only show a permanently |
| 165 | + empty value the owner is not meant to see. --> |
119 | 166 | #set($hasAgg = false) |
120 | 167 | #foreach($property in $properties) |
121 | | -#if($property.aggregate == "true")#set($hasAgg = true)#end |
| 168 | +#if($property.aggregate == "true" && !$property.sensitiveProperty)#set($hasAgg = true)#end |
122 | 169 | #end |
123 | 170 | #if($hasAgg) |
124 | 171 | <div class="hbox justify-end" style="max-width: 900px" x-show="itemsEnabled"> |
125 | 172 | <div x-h-card class="min-w-3xs"> |
126 | 173 | <div x-h-card-content class="vbox gap-1"> |
127 | 174 | #foreach($property in $properties) |
128 | | -#if($property.aggregate == "true") |
| 175 | +#if($property.aggregate == "true" && !$property.sensitiveProperty) |
129 | 176 | <div class="hbox items-baseline justify-between gap-4"> |
130 | 177 | <span class="text-sm text-secondary-foreground" x-text="T('$projectName:${tprefix}.t.${property.dataName}', '#if($property.widgetLabel)${property.widgetLabel}#else${property.name}#end')"></span> |
131 | 178 | <span class="text-sm tabular-nums" x-text="window.HarmoniaFormat ? window.HarmoniaFormat.number(form.${property.name}) : form.${property.name}"></span> |
|
0 commit comments