feat: improvements on ui

This commit is contained in:
Paulo Castellano 2026-01-21 18:55:33 -03:00
parent 1b7e161daa
commit b03ff582c0
7 changed files with 50 additions and 37 deletions

View file

@ -40,6 +40,7 @@ public function store(Request $request): RedirectResponse
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', Rules\Password::defaults()],
'timezone' => ['nullable', 'string', 'timezone'],
]);
// Check if registering via invite link (redirect contains /invites/)
@ -61,7 +62,7 @@ public function store(Request $request): RedirectResponse
$workspace = Workspace::create([
'user_id' => $user->id,
'name' => $user->name."'s Workspace",
'timezone' => 'UTC',
'timezone' => $request->input('timezone', 'UTC'),
]);
// Add user as owner member

View file

@ -32,6 +32,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
useSidebar,
} from '@/components/ui/sidebar';
import { useAppearance } from '@/composables/useAppearance';
import { useInitials } from '@/composables/useInitials';
@ -60,6 +61,7 @@ const workspaces = computed<Workspace[]>(() => page.props.workspaces as Workspac
const { getInitials } = useInitials();
const { appearance, updateAppearance } = useAppearance();
const { state: sidebarState } = useSidebar();
const themeLabels: Record<string, string> = {
light: 'Light',
@ -209,7 +211,7 @@ function handleLogout() {
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{{ auth.user.name }}</span>
<span class="truncate text-xs text-muted-foreground">{{ auth.user.email
}}</span>
}}</span>
</div>
</div>
</DropdownMenuLabel>
@ -304,9 +306,9 @@ function handleLogout() {
<!-- Create Post Button -->
<div v-if="currentWorkspace" class="px-2 py-2">
<Link :href="createPost.url()">
<Button class="w-full">
<IconPlus class="mr-2 size-4" />
Create post
<Button :size="sidebarState === 'collapsed' ? 'icon' : 'default'" class="w-full">
<IconPlus class="size-4" />
<span v-if="sidebarState === 'expanded'">Create post</span>
</Button>
</Link>
</div>

View file

@ -15,6 +15,9 @@ defineProps<{
email?: string | null;
redirect?: string | null;
}>();
// Get user's timezone from browser
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
</script>
<template>
@ -31,6 +34,7 @@ defineProps<{
class="flex flex-col gap-6"
>
<input v-if="redirect" type="hidden" name="redirect" :value="redirect" />
<input type="hidden" name="timezone" :value="timezone" />
<div class="grid gap-6">
<div class="grid gap-2">
<Label for="name">Name</Label>

View file

@ -224,7 +224,7 @@ const formatTime = (scheduledAt: string): string => {
</Tabs>
<Link :href="createPost.url()">
<Button>
<IconPlus class="mr-2 h-4 w-4" />
<IconPlus class="h-4 w-4" />
New Post
</Button>
</Link>

View file

@ -770,21 +770,20 @@ const deletePost = () => {
<span class="text-muted-foreground">|</span>
<Button type="button" variant="outline" size="icon-sm" @click="deletePost"
:disabled="isSaving || isSubmitting"
class="text-muted-foreground hover:text-destructive">
:disabled="isSaving || isSubmitting" class="text-muted-foreground hover:text-destructive">
<IconTrash class="h-4 w-4" />
</Button>
<Button type="button" variant="secondary" size="sm" :disabled="!canSubmit || isSubmitting || isSaving"
@click="submit('scheduled')">
<IconClock class="mr-2 h-4 w-4" />
Schedule
<Button type="button" variant="secondary" size="icon-sm" class="xl:w-auto xl:px-3"
:disabled="!canSubmit || isSubmitting || isSaving" @click="submit('scheduled')">
<IconClock class="h-4 w-4" />
<span class="hidden xl:inline">Schedule</span>
</Button>
<Button type="button" size="sm" :disabled="!canSubmit || isSubmitting || isSaving"
@click="submit('publishing')">
<IconSend class="mr-2 h-4 w-4" />
Publish
<Button type="button" size="icon-sm" class="xl:w-auto xl:px-3"
:disabled="!canSubmit || isSubmitting || isSaving" @click="submit('publishing')">
<IconSend class="h-4 w-4" />
<span class="hidden xl:inline">Publish</span>
</Button>
</div>
@ -1012,10 +1011,8 @@ const deletePost = () => {
<div v-else class="h-10 w-10 rounded-full bg-muted flex items-center justify-center">
<span class="text-sm font-medium">{{ pp.social_account.display_name?.charAt(0) }}</span>
</div>
<div
class="absolute -bottom-1 -right-1 h-5 w-5 rounded-full bg-background ring-2 ring-background flex items-center justify-center">
<component :is="getPlatformIcon(pp.platform)" class="h-4 w-4 text-neutral-600" />
</div>
<img :src="getPlatformLogo(pp.platform)" :alt="pp.platform"
class="absolute -bottom-0.5 -right-0.5 h-4 w-4 rounded-full ring-2 ring-background" />
</div>
<div class="min-w-0">
<p class="font-medium text-sm truncate">{{ pp.social_account.display_name }}</p>

View file

@ -148,6 +148,7 @@ const handleDelete = (post: Post) => {
</script>
<template>
<Head :title="pageTitle" />
<AppLayout :breadcrumbs="breadcrumbs">
@ -181,11 +182,12 @@ const handleDelete = (post: Post) => {
<IconFileText class="h-12 w-12 text-muted-foreground" />
<h3 class="mt-4 text-lg font-semibold">No posts found</h3>
<p class="mt-2 text-sm text-muted-foreground">
{{ currentStatus ? `No ${currentStatus} posts yet.` : 'Start by creating your first post.' }}
{{ currentStatus ? `No ${currentStatus} posts yet.` : 'Start by creating your first post.'
}}
</p>
<Link v-if="!currentStatus" :href="createPost.url()" class="mt-4">
<Button>
<IconPlus class="mr-2 h-4 w-4" />
<IconPlus class="h-4 w-4" />
Create Post
</Button>
</Link>
@ -214,15 +216,12 @@ const handleDelete = (post: Post) => {
<div class="flex items-center gap-2">
<div class="flex -space-x-2">
<img
v-for="pp in getEnabledPlatforms(post).slice(0, 5)"
:key="pp.id"
:src="getPlatformLogo(pp.platform)"
:alt="pp.platform"
class="h-6 w-6 rounded-full ring-2 ring-background"
/>
<img v-for="pp in getEnabledPlatforms(post).slice(0, 5)" :key="pp.id"
:src="getPlatformLogo(pp.platform)" :alt="pp.platform"
class="h-6 w-6 rounded-full ring-2 ring-background" />
</div>
<span v-if="getEnabledPlatforms(post).length > 5" class="text-xs text-muted-foreground">
<span v-if="getEnabledPlatforms(post).length > 5"
class="text-xs text-muted-foreground">
+{{ getEnabledPlatforms(post).length - 5 }}
</span>
<span class="text-xs text-muted-foreground ml-2">
@ -242,12 +241,7 @@ const handleDelete = (post: Post) => {
<IconPencil class="h-4 w-4" />
</Button>
</Link>
<Button
v-if="canEdit(post)"
variant="ghost"
size="icon"
@click="handleDelete(post)"
>
<Button v-if="canEdit(post)" variant="ghost" size="icon" @click="handleDelete(post)">
<IconTrash class="h-4 w-4 text-red-500" />
</Button>
</div>
@ -285,4 +279,4 @@ const handleDelete = (post: Post) => {
</div>
</div>
</AppLayout>
</template>
</template>

View file

@ -32,6 +32,21 @@
expect($user)->not->toBeNull();
expect($user->workspaces)->toHaveCount(1);
expect($user->workspaces->first()->name)->toBe("Test User's Workspace");
expect($user->workspaces->first()->timezone)->toBe('UTC');
});
test('new users workspace uses provided timezone', function () {
$this->post(route('register.store'), [
'name' => 'Test User',
'email' => 'test@example.com',
'password' => 'Password123!',
'timezone' => 'America/Sao_Paulo',
]);
$user = User::where('email', 'test@example.com')->first();
expect($user)->not->toBeNull();
expect($user->workspaces->first()->timezone)->toBe('America/Sao_Paulo');
});
test('new users do not have verified email by default', function () {