feat: improvements on ui
This commit is contained in:
parent
1b7e161daa
commit
b03ff582c0
7 changed files with 50 additions and 37 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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 () {
|
||||
|
|
|
|||
Loading…
Reference in a new issue