better system messages, display name relocation
This commit is contained in:
@@ -107,11 +107,11 @@ function formatTime(isoString: string): string {
|
||||
|
||||
<!-- Unread Separator Line -->
|
||||
<div
|
||||
v-if="entity.context_activities.length > 0"
|
||||
v-if="entity.new_activities.length > 0"
|
||||
class="c-dashboard__divider"
|
||||
>
|
||||
<span class="c-dashboard__divider-text"
|
||||
>New Activity Below</span
|
||||
>NEW</span
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -129,7 +129,6 @@ function formatTime(isoString: string): string {
|
||||
<span class="c-dashboard__activity-time">
|
||||
{{ formatTime(activity.created_at) }}
|
||||
</span>
|
||||
<span class="c-dashboard__new-badge">NEW</span>
|
||||
</div>
|
||||
<p class="c-dashboard__activity-desc">
|
||||
{{ activity.content }}
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
import Chat from '@/components/Chat.vue';
|
||||
import ParticipantsList from '@/components/ParticipantsList.vue';
|
||||
import LedgerList from '@/components/LedgerList.vue';
|
||||
import DisplayNameForm from '@/components/DisplayNameForm.vue';
|
||||
import { Head, Link as InertiaLink, usePage } from '@inertiajs/vue3';
|
||||
import { Head, Link as InertiaLink } from '@inertiajs/vue3';
|
||||
import { route } from 'ziggy-js';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
dynamic: {
|
||||
@@ -25,17 +23,6 @@ const props = defineProps<{
|
||||
isOwner: boolean;
|
||||
}>();
|
||||
|
||||
const currentUser = computed(() => {
|
||||
const page = usePage();
|
||||
const authUser = page.props.auth.user;
|
||||
const participant = props.dynamic.participants.find(p => p.id === authUser.id);
|
||||
return {
|
||||
id: authUser.id,
|
||||
name: authUser.name,
|
||||
display_name: participant?.pivot?.display_name ?? null,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
const breadcrumbs = [
|
||||
{
|
||||
@@ -71,7 +58,7 @@ const breadcrumbs = [
|
||||
</div>
|
||||
|
||||
<!-- Dynamic Chat -->
|
||||
<Chat :chat="dynamic.chat" />
|
||||
<Chat :chat="dynamic.chat" :participants="dynamic.participants" :dynamic-id="dynamic.id" />
|
||||
|
||||
<!-- Participants Component -->
|
||||
<ParticipantsList :participants="dynamic.participants" />
|
||||
@@ -79,9 +66,6 @@ const breadcrumbs = [
|
||||
<!-- Ledgers List Component -->
|
||||
<LedgerList :dynamic-id="dynamic.id" :ledgers="dynamic.ledgers" />
|
||||
|
||||
<!-- Display Name Form -->
|
||||
<DisplayNameForm :dynamic-id="dynamic.id" :participant="currentUser" />
|
||||
|
||||
<div v-if="isOwner" class="mt-8 flex gap-4">
|
||||
<InertiaLink :href="route('dynamics.invitations.create', dynamic.id)" class="c-dynamic-show__action-btn">
|
||||
Invite User
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
<script setup lang="ts">
|
||||
import { Head, Link as InertiaLink } from '@inertiajs/vue3';
|
||||
import { route } from 'ziggy-js';
|
||||
|
||||
const props = defineProps<{
|
||||
dynamic: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
participant: {
|
||||
id: number;
|
||||
name: string;
|
||||
display_name: string | null;
|
||||
role: string;
|
||||
};
|
||||
mutations: Array<{
|
||||
id: number;
|
||||
ledger_id: number;
|
||||
ledger: {
|
||||
id: number;
|
||||
name: string;
|
||||
alignment: string;
|
||||
};
|
||||
amount: number;
|
||||
description: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
}>;
|
||||
}>();
|
||||
|
||||
const breadcrumbs = [
|
||||
{
|
||||
name: 'Dynamics',
|
||||
href: route('dynamics.index'),
|
||||
},
|
||||
{
|
||||
name: props.dynamic.name,
|
||||
href: route('dynamics.show', props.dynamic.id),
|
||||
},
|
||||
{
|
||||
name: props.participant.display_name ?? props.participant.name,
|
||||
href: route('dynamics.users.show', [props.dynamic.id, props.participant.id]),
|
||||
},
|
||||
];
|
||||
|
||||
function getAmountClass(amount: number, alignment: string): string {
|
||||
if (alignment === 'positive') {
|
||||
return amount > 0
|
||||
? 'c-participant-show__activity-amount--positive'
|
||||
: 'c-participant-show__activity-amount--negative';
|
||||
}
|
||||
|
||||
if (alignment === 'negative') {
|
||||
return amount < 0
|
||||
? 'c-participant-show__activity-amount--positive'
|
||||
: 'c-participant-show__activity-amount--negative';
|
||||
}
|
||||
|
||||
return 'c-participant-show__activity-amount--neutral';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head :title="participant.display_name ?? participant.name" />
|
||||
|
||||
<div class="c-participant-show">
|
||||
<div class="c-participant-show__container">
|
||||
<!-- Header Card -->
|
||||
<div class="c-participant-show__card">
|
||||
<div class="c-participant-show__body">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<span class="c-participant-show__role-badge">
|
||||
{{ participant.role }}
|
||||
</span>
|
||||
<h3 class="c-participant-show__title mt-1">
|
||||
{{ participant.display_name ?? participant.name }}
|
||||
</h3>
|
||||
<p v-if="participant.display_name" class="c-participant-show__subtitle">
|
||||
Real Name: {{ participant.name }}
|
||||
</p>
|
||||
</div>
|
||||
<InertiaLink :href="route('dynamics.show', dynamic.id)" class="c-participant-show__back-btn">
|
||||
Back to Dynamic
|
||||
</InertiaLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity Block -->
|
||||
<div class="c-participant-show__activity mt-8">
|
||||
<h4 class="c-participant-show__activity-title">Recent Activity</h4>
|
||||
|
||||
<div v-if="mutations.length > 0" class="c-participant-show__activity-list mt-4">
|
||||
<div
|
||||
v-for="mutation in mutations"
|
||||
:key="mutation.id"
|
||||
class="c-participant-show__activity-item"
|
||||
>
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<InertiaLink
|
||||
:href="route('dynamics.ledgers.show', [dynamic.id, mutation.ledger_id])"
|
||||
class="c-participant-show__activity-ledger"
|
||||
>
|
||||
{{ mutation.ledger.name }}
|
||||
</InertiaLink>
|
||||
<p class="c-participant-show__activity-desc mt-1">
|
||||
{{ mutation.description }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<span
|
||||
:class="getAmountClass(mutation.amount, mutation.ledger.alignment)"
|
||||
class="c-participant-show__activity-amount"
|
||||
>
|
||||
{{ mutation.amount > 0 ? '+' : '' }}{{ mutation.amount }}
|
||||
</span>
|
||||
<div class="c-participant-show__activity-meta mt-1">
|
||||
<span
|
||||
:class="{
|
||||
'c-participant-show__activity-status--pending': mutation.status === 'pending',
|
||||
'c-participant-show__activity-status--approved': mutation.status === 'approved',
|
||||
'c-participant-show__activity-status--rejected': mutation.status === 'rejected',
|
||||
}"
|
||||
class="c-participant-show__activity-status"
|
||||
>
|
||||
{{ mutation.status }}
|
||||
</span>
|
||||
<span class="c-participant-show__activity-time ml-2">
|
||||
{{ new Date(mutation.created_at).toLocaleDateString() }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="c-participant-show__empty mt-4">
|
||||
No recent activity found for this participant.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@reference "../../../css/app.css";
|
||||
|
||||
.c-participant-show {
|
||||
@apply py-12;
|
||||
}
|
||||
|
||||
.c-participant-show__container {
|
||||
@apply mx-auto max-w-7xl sm:px-6 lg:px-8;
|
||||
}
|
||||
|
||||
.c-participant-show__card {
|
||||
@apply overflow-hidden;
|
||||
background-color: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.c-participant-show__body {
|
||||
@apply p-6;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.c-participant-show__role-badge {
|
||||
@apply inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold uppercase tracking-wider;
|
||||
background-color: var(--primary);
|
||||
color: var(--primary-foreground);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.c-participant-show__title {
|
||||
@apply text-2xl font-bold;
|
||||
}
|
||||
|
||||
.c-participant-show__subtitle {
|
||||
@apply mt-1 text-sm;
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
.c-participant-show__back-btn {
|
||||
@apply inline-flex items-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-xs font-semibold tracking-widest text-white uppercase transition duration-150 ease-in-out hover:bg-gray-700 focus:bg-gray-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none active:bg-gray-900 dark:bg-gray-200 dark:text-gray-800 dark:hover:bg-white dark:focus:bg-white dark:focus:ring-offset-gray-800 dark:active:bg-gray-300;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-title {
|
||||
@apply text-lg font-medium;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.c-participant-show__activity-list {
|
||||
@apply space-y-4;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-item {
|
||||
@apply p-4;
|
||||
background-color: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.c-participant-show__activity-ledger {
|
||||
@apply font-semibold hover:underline text-sm;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.c-participant-show__activity-desc {
|
||||
@apply text-sm;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.c-participant-show__activity-amount {
|
||||
@apply font-bold text-sm;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-amount--positive {
|
||||
@apply text-green-500;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-amount--negative {
|
||||
@apply text-red-500;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-amount--neutral {
|
||||
@apply text-neutral-500;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-meta {
|
||||
@apply flex items-center justify-end text-xs;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-status {
|
||||
@apply rounded px-1.5 py-0.5 text-[9px] font-medium tracking-wider uppercase;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-status--pending {
|
||||
@apply bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-status--approved {
|
||||
@apply bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-status--rejected {
|
||||
@apply bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400;
|
||||
}
|
||||
|
||||
.c-participant-show__activity-time {
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
.c-participant-show__empty {
|
||||
@apply text-sm text-center py-8;
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
</style>
|
||||
@@ -6,6 +6,7 @@ import ProfileController from '@/actions/App/Http/Controllers/Settings/ProfileCo
|
||||
import DeleteUser from '@/components/DeleteUser.vue';
|
||||
import Heading from '@/components/Heading.vue';
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import DynamicDisplayNameItem from '@/components/DynamicDisplayNameItem.vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
@@ -23,6 +24,18 @@ defineOptions({
|
||||
},
|
||||
});
|
||||
|
||||
defineProps<{
|
||||
mustVerifyEmail: boolean;
|
||||
status: string | null;
|
||||
dynamics: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
pivot: {
|
||||
display_name: string | null;
|
||||
};
|
||||
}>;
|
||||
}>();
|
||||
|
||||
const page = usePage();
|
||||
const user = computed(() => page.props.auth.user);
|
||||
</script>
|
||||
@@ -99,6 +112,27 @@ const user = computed(() => page.props.auth.user);
|
||||
>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<!-- Dynamic-Specific Display Names Section -->
|
||||
<div class="pt-8 border-t">
|
||||
<Heading
|
||||
variant="small"
|
||||
title="Dynamic Display Names"
|
||||
description="Customize your display name for each of your dynamics"
|
||||
/>
|
||||
<div class="mt-6 space-y-4">
|
||||
<div v-if="dynamics && dynamics.length > 0" class="space-y-4">
|
||||
<DynamicDisplayNameItem
|
||||
v-for="dyn in dynamics"
|
||||
:key="dyn.id"
|
||||
:dynamic="dyn"
|
||||
/>
|
||||
</div>
|
||||
<p v-else class="text-sm text-muted-foreground">
|
||||
You are not a participant in any dynamics yet.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DeleteUser />
|
||||
|
||||
Reference in New Issue
Block a user