front-end restructuring, chat shows message ownership
linter / quality (push) Failing after 1m1s
tests / ci (8.3) (push) Failing after 49s
tests / ci (8.4) (push) Failing after 1m4s
tests / ci (8.5) (push) Failing after 1m4s

This commit is contained in:
Daan Meijer
2026-06-16 14:13:08 +02:00
parent d44bcf6fda
commit 4e9e6dce2a
31 changed files with 516 additions and 404 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ configureEcho({
forceTLS: false,
enabledTransports: ['ws', 'wss'],
});
if (window) {
if (typeof window !== 'undefined') {
(window as any).echoConfigured = true;
}
-8
View File
@@ -22,11 +22,3 @@ const className = computed(() => props.class);
<slot />
</main>
</template>
<style scoped>
@reference "../../css/app.css";
.app-content {
@apply mx-auto flex h-full w-full max-w-7xl flex-1 flex-col gap-4 rounded-xl;
}
</style>
-20
View File
@@ -10,23 +10,3 @@ import AppLogoIcon from '@/components/AppLogoIcon.vue';
<span class="app-logo__text">Laravel Starter Kit</span>
</div>
</template>
<style scoped>
@reference "../../css/app.css";
.app-logo__icon-container {
@apply flex aspect-square size-8 items-center justify-center rounded-md bg-sidebar-primary text-sidebar-primary-foreground;
}
.app-logo__icon {
@apply size-5 fill-current text-white dark:text-black;
}
.app-logo__text-container {
@apply ml-1 grid flex-1 text-left text-sm;
}
.app-logo__text {
@apply mb-0.5 truncate leading-tight font-semibold;
}
</style>
@@ -23,15 +23,3 @@ withDefaults(
</div>
</header>
</template>
<style scoped>
@reference "../../css/app.css";
.sidebar-header {
@apply flex h-16 shrink-0 items-center gap-2 border-b border-sidebar-border/70 px-6 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 md:px-4;
}
.sidebar-header__inner {
@apply flex items-center gap-2;
}
</style>
@@ -27,27 +27,3 @@ const tabs = [
</button>
</div>
</template>
<style scoped>
@reference "../../css/app.css";
.appearance-tabs {
@apply inline-flex gap-1 rounded-lg bg-neutral-100 p-1 dark:bg-neutral-800;
}
.appearance-tabs__tab {
@apply flex items-center rounded-md px-3.5 py-1.5 text-neutral-500 transition-colors hover:bg-neutral-200/60 hover:text-black dark:text-neutral-400 dark:hover:bg-neutral-700/60;
}
.appearance-tabs__tab--active {
@apply bg-white shadow-xs hover:bg-white dark:bg-neutral-700 dark:text-neutral-100 dark:hover:bg-neutral-700;
}
.appearance-tabs__icon {
@apply -ml-1 h-4 w-4;
}
.appearance-tabs__label {
@apply ml-1.5 text-sm;
}
</style>
+18 -171
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useForm } from '@inertiajs/vue3';
import { ref, computed } from 'vue';
import { useForm, usePage } from '@inertiajs/vue3';
import { useEcho, echoIsConfigured, configureEcho } from '@laravel/echo-vue';
import { route } from 'ziggy-js';
import { Paperclip, Info } from '@lucide/vue';
@@ -10,7 +10,7 @@ const props = defineProps<{
id: number;
messages: Array<{
id: number;
user: { name: string };
user: { id: number; name: string };
content: string;
created_at: string;
media?: Array<{
@@ -63,6 +63,12 @@ function removeFile(index: number) {
form.media.splice(index, 1);
}
const currentUser = computed(() => usePage().props.auth?.user);
function isOwnMessage(messageUserId: number): boolean {
return currentUser.value && currentUser.value.id === messageUserId;
}
function submit() {
form.post(route('chats.messages.store', props.chat.id), {
onSuccess: () => {
@@ -103,6 +109,10 @@ function closeLightbox() {
{
'c-chat__message--system':
message.content.startsWith('System:'),
'c-chat__message--own': isOwnMessage(message.user.id),
'c-chat__message--other': !isOwnMessage(
message.user.id,
),
},
]"
>
@@ -240,185 +250,22 @@ function closeLightbox() {
</form>
<!-- Gorgeous Dark Lightbox Modal -->
<div
v-if="activeLightboxUrl"
class="c-chat__lightbox"
@click="closeLightbox"
>
<button @click="closeLightbox" class="c-chat__lightbox-close">
</button>
<div class="c-chat__lightbox-content" @click.stop>
<div v-if="activeLightboxUrl" class="c-lightbox" @click="closeLightbox">
<button @click="closeLightbox" class="c-lightbox__close"></button>
<div class="c-lightbox__content" @click.stop>
<img
v-if="activeLightboxType === 'image'"
:src="activeLightboxUrl"
class="c-chat__lightbox-image"
class="c-lightbox__image"
/>
<video
v-else-if="activeLightboxType === 'video'"
:src="activeLightboxUrl"
controls
autoplay
class="c-chat__lightbox-video"
class="c-lightbox__video"
></video>
</div>
</div>
</div>
</template>
<style scoped>
@reference "../../css/app.css";
.c-chat {
@apply mt-8;
}
.c-chat__title {
@apply text-lg font-medium text-gray-900 dark:text-gray-100;
}
.c-chat__list {
@apply mt-4 space-y-4;
}
.c-chat__message {
@apply overflow-hidden bg-white p-4 shadow-sm sm:rounded-lg dark:bg-gray-800;
}
.c-chat__message--system {
@apply border-0 bg-transparent p-0 shadow-none;
}
.c-chat__system-inner {
@apply flex items-center gap-2 rounded-md border border-neutral-200/60 bg-neutral-50 px-3 py-1.5 text-xs text-neutral-500 dark:border-neutral-800/40 dark:bg-neutral-900/10 dark:text-neutral-400;
}
.c-chat__system-icon {
@apply size-3.5 shrink-0 text-neutral-400;
}
.c-chat__system-text {
@apply flex-1 font-medium;
}
.c-chat__system-time {
@apply shrink-0 text-[10px] text-neutral-400;
}
.c-chat__message-header {
@apply flex justify-between;
}
.c-chat__message-author {
@apply font-semibold;
}
.c-chat__message-time {
@apply text-xs text-gray-500;
}
.c-chat__message-text {
@apply mt-2 text-sm text-gray-600 dark:text-gray-400;
}
.c-chat__message-media {
@apply mt-3 flex flex-wrap gap-2;
}
.c-chat__media-item {
@apply relative max-w-[240px] overflow-hidden rounded-md border border-neutral-200 bg-black dark:border-neutral-700;
}
.c-chat__image {
@apply h-auto max-h-[180px] w-full object-cover;
}
.c-chat__video {
@apply h-auto max-h-[180px] w-full;
}
.c-chat__play-overlay {
@apply absolute inset-0 flex items-center justify-center bg-black/40 text-2xl font-bold text-white;
}
.c-chat__empty {
@apply text-gray-500;
}
.c-chat__form {
@apply mt-6 space-y-6;
}
.c-chat__form-group {
@apply space-y-2;
}
.c-chat__label {
@apply block text-sm font-medium text-gray-700 dark:text-gray-300;
}
.c-chat__textarea {
@apply mt-1 block w-full rounded-md 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-chat__error {
@apply text-sm text-red-600;
}
.c-chat__attachment-container {
@apply mt-2 flex items-center;
}
.c-chat__attach-btn {
@apply inline-flex cursor-pointer items-center gap-1.5 text-xs text-neutral-500 transition-colors hover:text-foreground;
}
.c-chat__attach-icon {
@apply size-3.5;
}
.c-chat__preview-list {
@apply mt-2 flex flex-wrap gap-2;
}
.c-chat__preview-item {
@apply relative inline-flex items-center gap-2 rounded border border-neutral-200 bg-neutral-100 p-1.5 pr-8 text-xs dark:border-neutral-700 dark:bg-neutral-800;
}
.c-chat__preview-name {
@apply max-w-[150px] truncate;
}
.c-chat__preview-remove {
@apply absolute top-1.5 right-1.5 cursor-pointer text-[10px] text-neutral-400 hover:text-red-500;
}
.c-chat__submit-box {
@apply flex items-center gap-4;
}
.c-chat__button {
@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;
}
/* Lightbox Styling */
.c-chat__lightbox {
@apply fixed inset-0 z-50 flex items-center justify-center bg-black/95 p-4;
}
.c-chat__lightbox-close {
@apply absolute top-6 right-6 cursor-pointer text-3xl text-white transition-colors duration-200 hover:text-red-500;
}
.c-chat__lightbox-content {
@apply max-h-full max-w-full;
}
.c-chat__lightbox-image {
@apply max-h-[90vh] max-w-full rounded object-contain shadow-lg;
}
.c-chat__lightbox-video {
@apply max-h-[90vh] max-w-full rounded shadow-lg;
}
</style>
+20 -6
View File
@@ -168,11 +168,15 @@ function submit() {
}
.c-create-ledger-form__card {
@apply overflow-hidden bg-white shadow-sm sm:rounded-lg dark:bg-gray-800;
@apply overflow-hidden;
background-color: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.c-create-ledger-form__body {
@apply p-6 text-gray-900 dark:text-gray-100;
@apply p-6;
color: var(--foreground);
}
.c-create-ledger-form__title {
@@ -188,19 +192,29 @@ function submit() {
}
.c-create-ledger-form__label {
@apply block text-sm font-medium text-gray-700 dark:text-gray-300;
@apply block text-sm font-medium;
color: var(--foreground);
}
.c-create-ledger-form__input {
@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;
@apply mt-1 block w-full rounded-md border shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:focus:border-indigo-600 dark:focus:ring-indigo-600;
border-color: var(--border);
background-color: var(--background);
color: var(--foreground);
}
.c-create-ledger-form__textarea {
@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;
@apply mt-1 block w-full rounded-md border shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:focus:border-indigo-600 dark:focus:ring-indigo-600;
border-color: var(--border);
background-color: var(--background);
color: var(--foreground);
}
.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;
@apply mt-1 block w-full rounded-md border p-2 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:focus:border-indigo-600 dark:focus:ring-indigo-600;
border-color: var(--border);
background-color: var(--background);
color: var(--foreground);
}
.c-create-ledger-form__file-input {
-24
View File
@@ -20,27 +20,3 @@ withDefaults(defineProps<Props>(), {
</p>
</header>
</template>
<style scoped>
@reference "../../css/app.css";
.c-heading {
@apply mb-8 space-y-0.5;
}
.c-heading--small {
@apply mb-0 space-y-0;
}
.c-heading__title {
@apply text-xl font-semibold tracking-tight;
}
.c-heading--small .c-heading__title {
@apply mb-0.5 text-base font-medium tracking-normal;
}
.c-heading__description {
@apply text-sm text-muted-foreground;
}
</style>
-8
View File
@@ -11,11 +11,3 @@ defineProps<{
</p>
</div>
</template>
<style scoped>
@reference "../../css/app.css";
.c-input-error__message {
@apply text-sm text-red-600 dark:text-red-500;
}
</style>
+9 -3
View File
@@ -105,7 +105,8 @@ defineProps<{
}
.c-ledger-list__title {
@apply text-lg font-medium text-gray-900 dark:text-gray-100;
@apply text-lg font-medium;
color: var(--foreground);
}
.c-ledger-list__grid {
@@ -113,15 +114,20 @@ defineProps<{
}
.c-ledger-list__item {
@apply border-b border-gray-200 bg-white p-6 dark:border-gray-600 dark:bg-gray-700;
@apply p-6;
background-color: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.c-ledger-list__item-name {
@apply text-lg font-semibold;
color: var(--foreground);
}
.c-ledger-list__item-score {
@apply mt-2 text-sm text-gray-600 dark:text-gray-400;
@apply mt-2 text-sm;
color: var(--muted-foreground);
}
.c-ledger-list__alignment-wrapper {
+7 -2
View File
@@ -30,7 +30,8 @@ defineProps<{
}
.c-participants-list__title {
@apply text-lg font-medium text-gray-900 dark:text-gray-100;
@apply text-lg font-medium;
color: var(--foreground);
}
.c-participants-list__grid {
@@ -38,6 +39,10 @@ defineProps<{
}
.c-participants-list__item {
@apply overflow-hidden bg-white p-4 shadow-sm sm:rounded-lg dark:bg-gray-800;
@apply overflow-hidden p-4;
background-color: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--foreground);
}
</style>
-20
View File
@@ -40,23 +40,3 @@ defineExpose({
</button>
</div>
</template>
<style scoped>
@reference "../../css/app.css";
.password-input {
@apply relative;
}
:deep(.password-input__field) {
@apply pr-10;
}
.password-input__toggle {
@apply absolute inset-y-0 right-0 flex items-center rounded-r-md px-3 text-muted-foreground hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring focus-visible:outline-none;
}
.password-input__icon {
@apply size-4;
}
</style>
-8
View File
@@ -23,11 +23,3 @@ defineProps<Props>();
<slot />
</Link>
</template>
<style scoped>
@reference "../../css/app.css";
.c-text-link {
@apply text-foreground underline decoration-neutral-300 underline-offset-4 transition-colors duration-300 ease-out hover:decoration-current! dark:decoration-neutral-500;
}
</style>
-20
View File
@@ -34,23 +34,3 @@ const showAvatar = computed(
<span v-if="showEmail" class="user-info__email">{{ user.email }}</span>
</div>
</template>
<style scoped>
@reference "../../css/app.css";
.user-info__avatar {
@apply h-8 w-8 overflow-hidden rounded-lg;
}
.user-info__details {
@apply grid flex-1 text-left text-sm leading-tight;
}
.user-info__name {
@apply truncate font-medium;
}
.user-info__email {
@apply truncate text-xs text-muted-foreground;
}
</style>
@@ -32,47 +32,3 @@ defineProps<{
</div>
</div>
</template>
<style scoped>
@reference "../../../css/app.css";
.auth-layout {
@apply flex min-h-svh flex-col items-center justify-center gap-6 bg-background p-6 md:p-10;
}
.auth-layout__container {
@apply w-full max-w-sm;
}
.auth-layout__inner {
@apply flex flex-col gap-8;
}
.auth-layout__header {
@apply flex flex-col items-center gap-4;
}
.auth-layout__logo-link {
@apply flex flex-col items-center gap-2 font-medium;
}
.auth-layout__logo-box {
@apply mb-1 flex h-9 w-9 items-center justify-center rounded-md;
}
.auth-layout__logo {
@apply size-9 fill-current text-[var(--foreground)] dark:text-white;
}
.auth-layout__title-box {
@apply space-y-2 text-center;
}
.auth-layout__title {
@apply text-xl font-medium;
}
.auth-layout__description {
@apply text-center text-sm text-muted-foreground;
}
</style>
+6 -2
View File
@@ -76,11 +76,15 @@ const breadcrumbs = [
}
.c-dynamic-show__card {
@apply overflow-hidden bg-white shadow-sm sm:rounded-lg dark:bg-gray-800;
@apply overflow-hidden;
background-color: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.c-dynamic-show__body {
@apply p-6 text-gray-900 dark:text-gray-100;
@apply p-6;
color: var(--foreground);
}
.c-dynamic-show__title {
+5 -31
View File
@@ -250,26 +250,20 @@ function isOwnerUser(userId: number): boolean {
</div>
<!-- Lightbox Modal -->
<div
v-if="activeLightboxUrl"
class="c-ledger-show__lightbox"
@click="closeLightbox"
>
<button @click="closeLightbox" class="c-ledger-show__lightbox-close">
</button>
<div class="c-ledger-show__lightbox-content" @click.stop>
<div v-if="activeLightboxUrl" class="c-lightbox" @click="closeLightbox">
<button @click="closeLightbox" class="c-lightbox__close"></button>
<div class="c-lightbox__content" @click.stop>
<img
v-if="activeLightboxType === 'image'"
:src="activeLightboxUrl"
class="c-ledger-show__lightbox-img"
class="c-lightbox__image"
/>
<video
v-else-if="activeLightboxType === 'video'"
:src="activeLightboxUrl"
controls
autoplay
class="c-ledger-show__lightbox-video"
class="c-lightbox__video"
></video>
</div>
</div>
@@ -361,24 +355,4 @@ function isOwnerUser(userId: number): boolean {
.c-ledger-show__media-video-overlay {
@apply absolute inset-0 flex items-center justify-center bg-black/40 text-2xl font-bold text-white;
}
.c-ledger-show__lightbox {
@apply fixed inset-0 z-50 flex items-center justify-center bg-black/95 p-4;
}
.c-ledger-show__lightbox-close {
@apply absolute top-6 right-6 cursor-pointer text-3xl text-white transition-colors duration-200 hover:text-red-500;
}
.c-ledger-show__lightbox-content {
@apply max-h-full max-w-full;
}
.c-ledger-show__lightbox-img {
@apply max-h-[90vh] max-w-full rounded object-contain shadow-lg;
}
.c-ledger-show__lightbox-video {
@apply max-h-[90vh] max-w-full rounded shadow-lg;
}
</style>