feat: Implement Ledger alignment and correct corrective mutation authors in seeders

This commit is contained in:
Daan Meijer
2026-06-15 23:22:08 +02:00
parent bf7da48a58
commit 3dd7c48b08
9 changed files with 177 additions and 8 deletions
@@ -9,6 +9,7 @@ const props = defineProps<{
const form = useForm({
name: '',
rules: '',
alignment: 'neutral',
media: [] as File[],
});
@@ -80,6 +81,29 @@ function submit() {
</div>
</div>
<div class="c-create-ledger-form__field">
<label
for="alignment"
class="c-create-ledger-form__label"
>Alignment</label
>
<select
v-model="form.alignment"
id="alignment"
class="c-create-ledger-form__select"
>
<option value="positive">Positive (Higher Score is Better)</option>
<option value="neutral">Neutral (Frictionless / Standard)</option>
<option value="negative">Negative (Lower Score is Better / Demerits)</option>
</select>
<div
v-if="form.errors.alignment"
class="c-create-ledger-form__error"
>
{{ form.errors.alignment }}
</div>
</div>
<!-- Media Uploads for Ledgers -->
<div class="c-create-ledger-form__field">
<label
@@ -170,6 +194,10 @@ function submit() {
@apply mt-1 block w-full rounded-md border border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600;
}
.c-create-ledger-form__select {
@apply mt-1 block w-full rounded-md border border-gray-300 bg-white p-2 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600;
}
.c-create-ledger-form__file-input {
@apply mt-1 block w-full text-sm text-neutral-500 file:mr-4 file:rounded-md file:border-0 file:bg-indigo-50 file:px-4 file:py-2 file:text-xs file:font-semibold file:text-indigo-700 hover:file:bg-indigo-100;
}
+43
View File
@@ -8,6 +8,7 @@ defineProps<{
id: number;
name: string;
score: number;
alignment: string;
media?: Array<{ id: number; url: string; mime_type: string }>;
}>;
}>();
@@ -41,6 +42,28 @@ defineProps<{
Score: {{ ledger.score }}
</p>
<!-- Ledger Alignment Badge -->
<div class="c-ledger-list__alignment-wrapper">
<span
v-if="ledger.alignment === 'positive'"
class="c-ledger-list__alignment-badge c-ledger-list__alignment-badge--positive"
>
Higher is Better
</span>
<span
v-else-if="ledger.alignment === 'negative'"
class="c-ledger-list__alignment-badge c-ledger-list__alignment-badge--negative"
>
Lower is Better
</span>
<span
v-else
class="c-ledger-list__alignment-badge c-ledger-list__alignment-badge--neutral"
>
Neutral
</span>
</div>
<!-- Ledger Media Thumbnails -->
<div
v-if="ledger.media && ledger.media.length > 0"
@@ -103,6 +126,26 @@ defineProps<{
@apply mt-2 text-sm text-gray-600 dark:text-gray-400;
}
.c-ledger-list__alignment-wrapper {
@apply mt-2 flex items-center;
}
.c-ledger-list__alignment-badge {
@apply text-[10px] font-semibold uppercase tracking-wide px-1.5 py-0.5 rounded;
}
.c-ledger-list__alignment-badge--positive {
@apply text-green-600 bg-green-50/50 dark:text-green-400 dark:bg-green-950/20;
}
.c-ledger-list__alignment-badge--negative {
@apply text-red-600 bg-red-50/50 dark:text-red-400 dark:bg-red-950/20;
}
.c-ledger-list__alignment-badge--neutral {
@apply text-gray-500 bg-gray-50/50 dark:text-gray-400 dark:bg-neutral-800/20;
}
.c-ledger-list__media-list {
@apply mt-2 flex flex-wrap gap-1;
}
+22 -4
View File
@@ -6,6 +6,7 @@ import Chat from '@/components/Chat.vue';
const props = defineProps<{
dynamicId: number;
ledgerId: number;
ledgerAlignment?: string;
mutations: Array<{
id: number;
user_id: number;
@@ -44,6 +45,22 @@ function isOwnerUser(userId: number): boolean {
return participant?.pivot?.role === 'owner';
}
function getAmountClass(amount: number): string {
const alignment = props.ledgerAlignment || 'neutral';
if (alignment === 'positive') {
return amount > 0 ? 'c-mutation-list__item-amount--positive' : 'c-mutation-list__item-amount--negative';
}
if (alignment === 'negative') {
// Lower is better: negative amount is positive/favorable, positive amount is negative/unfavorable
return amount < 0 ? 'c-mutation-list__item-amount--positive' : 'c-mutation-list__item-amount--negative';
}
// Neutral alignment
return 'c-mutation-list__item-amount--neutral';
}
</script>
<template>
@@ -69,10 +86,7 @@ function isOwnerUser(userId: number): boolean {
</span>
<div class="c-mutation-list__item-meta">
<span
:class="{
'c-mutation-list__item-amount--positive': mutation.amount > 0,
'c-mutation-list__item-amount--negative': mutation.amount < 0,
}"
:class="getAmountClass(mutation.amount)"
class="c-mutation-list__item-amount"
>
{{ mutation.amount > 0 ? '+' : ''
@@ -210,6 +224,10 @@ function isOwnerUser(userId: number): boolean {
@apply text-red-500;
}
.c-mutation-list__item-amount--neutral {
@apply text-gray-500 dark:text-gray-400;
}
.c-mutation-list__item-status {
@apply rounded px-1.5 py-0.5 text-[10px] font-medium tracking-wider uppercase;
}
+44
View File
@@ -22,6 +22,7 @@ const props = defineProps<{
name: string;
score: number;
rules: string;
alignment: string;
media?: Array<{ id: number; url: string; mime_type: string }>;
mutations: Array<{
id: number;
@@ -175,6 +176,28 @@ function isOwnerUser(userId: number): boolean {
{{ ledger.rules }}
</p>
<!-- Ledger Alignment Badge / Subtitle -->
<div class="c-ledger-show__alignment-wrapper">
<span
v-if="ledger.alignment === 'positive'"
class="c-ledger-show__alignment-badge c-ledger-show__alignment-badge--positive"
>
Positive Alignment A higher score is better.
</span>
<span
v-else-if="ledger.alignment === 'negative'"
class="c-ledger-show__alignment-badge c-ledger-show__alignment-badge--negative"
>
Negative Alignment A lower score is better (demerits / infractions).
</span>
<span
v-else
class="c-ledger-show__alignment-badge c-ledger-show__alignment-badge--neutral"
>
Neutral Alignment Scorekeeping neutral.
</span>
</div>
<!-- Ledger Descriptive Media -->
<div
v-if="ledger.media && ledger.media.length > 0"
@@ -219,6 +242,7 @@ function isOwnerUser(userId: number): boolean {
:mutations="ledger.mutations"
:participants="dynamic.participants"
:is-owner="isOwner"
:ledger-alignment="ledger.alignment"
@open-lightbox="openLightbox"
/>
</div>
@@ -296,6 +320,26 @@ function isOwnerUser(userId: number): boolean {
@apply mt-2 text-sm text-gray-600 dark:text-gray-400;
}
.c-ledger-show__alignment-wrapper {
@apply mt-3 flex items-center;
}
.c-ledger-show__alignment-badge {
@apply text-xs font-semibold uppercase tracking-wide px-2.5 py-1 rounded;
}
.c-ledger-show__alignment-badge--positive {
@apply text-green-600 bg-green-50/50 dark:text-green-400 dark:bg-green-950/20;
}
.c-ledger-show__alignment-badge--negative {
@apply text-red-600 bg-red-50/50 dark:text-red-400 dark:bg-red-950/20;
}
.c-ledger-show__alignment-badge--neutral {
@apply text-gray-500 bg-gray-50/50 dark:text-gray-400 dark:bg-neutral-800/20;
}
.c-ledger-show__media-list {
@apply mt-4 flex flex-wrap gap-3;
}