Browse Source

Refactor UI components for improved styling and user experience

- Updated UserMenu.vue to enhance button and avatar styles for a more modern look.
- Adjusted alert component styles in index.ts for consistent rounded corners.
- Simplified loading state markup in order confirmation page for cleaner code.
- Enhanced button and link text for better clarity and user navigation.

These changes aim to improve the overall aesthetic and usability of the application, ensuring a more cohesive user experience.
main
Bastian Masanek 2 months ago
parent
commit
f6f09ca147
  1. 8
      app/components/UserMenu.vue
  2. 5
      app/components/ui/alert/index.ts
  3. 56
      app/pages/order/confirm/[orderId].vue
  4. 2
      server/middleware/rate-limit.ts

8
app/components/UserMenu.vue

@ -44,24 +44,24 @@ async function handleLogout() {
<template>
<!-- Not logged in: Show login prompt -->
<NuxtLink v-if="!loggedIn" to="/auth"
class="flex items-center gap-2 px-4 py-2 rounded-2xl border-2 border-dashed border-muted-foreground/30 hover:border-experimenta-accent hover:bg-experimenta-accent/10 transition-all"
class="flex items-center gap-2 px-4 py-2 rounded-[35px] border-2 border-dashed border-muted-foreground/30 hover:border-experimenta-accent hover:bg-experimenta-accent/10 transition-all"
aria-label="Anmelden oder Registrieren">
<Avatar class="h-10 w-10 border-2 border-muted-foreground/30 rounded-2xl">
<AvatarFallback class="bg-muted text-muted-foreground rounded-2xl">
<User class="h-5 w-5" />
</AvatarFallback>
</Avatar>
<span class="text-sm font-medium hidden sm:inline">Anmelden</span>
<span class="font-medium hidden sm:inline">Anmelden</span>
</NuxtLink>
<!-- Logged in: Show user menu -->
<DropdownMenu v-else>
<DropdownMenuTrigger as-child>
<button
class="flex items-center gap-3 rounded-2xl px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-experimenta-accent focus:ring-offset-2 transition-all hover:bg-white/10"
class="flex items-center gap-3 rounded-[35px] px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-experimenta-accent focus:ring-offset-2 transition-all hover:bg-white/10"
aria-label="Benutzermenü öffnen">
<!-- Greeting text (Desktop only) -->
<span class="hidden md:inline font-medium text-white">
<span class="hidden md:inline font-medium text-white pl-2">
Hallo, {{ user?.firstName }}
</span>

5
app/components/ui/alert/index.ts

@ -6,15 +6,14 @@ export { default as AlertDescription } from './AlertDescription.vue'
export { default as AlertTitle } from './AlertTitle.vue'
export const alertVariants = cva(
'relative w-full rounded-lg border p-4 flex items-center gap-3 [&>svg]:shrink-0 [&>svg]:text-foreground',
'relative w-full rounded-[25px] border p-4 flex items-center gap-3 [&>svg]:shrink-0 [&>svg]:text-foreground',
{
variants: {
variant: {
default: 'bg-background text-foreground',
destructive:
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
error:
'bg-white/15 border-l-4 border-warning text-white [&>svg]:text-warning',
error: 'bg-white/15 border-l-4 border-warning text-white [&>svg]:text-warning',
},
},
defaultVariants: {

56
app/pages/order/confirm/[orderId].vue

@ -119,9 +119,7 @@ onMounted(() => {
<!-- Loading State -->
<div v-if="isLoading" class="text-center py-12">
<div
class="animate-spin rounded-full h-12 w-12 border-4 border-white/20 border-t-white mx-auto mb-4"
/>
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white/20 border-t-white mx-auto mb-4" />
<p class="text-white/60">Lade Bestellung...</p>
</div>
@ -135,19 +133,11 @@ onMounted(() => {
<!-- Warning Notice -->
<Alert class="border-yellow-500/50 bg-yellow-500/10">
<div class="flex items-start gap-3">
<svg
class="w-5 h-5 text-yellow-500 mt-0.5 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
></path>
<svg class="w-5 h-5 text-yellow-500 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z">
</path>
</svg>
<div>
<AlertTitle class="text-yellow-500">Wichtiger Hinweis</AlertTitle>
@ -162,34 +152,18 @@ onMounted(() => {
<!-- Confirmation Button -->
<Card class="p-6">
<div class="space-y-4">
<Button
@click="confirmOrder"
:disabled="isConfirming"
variant="experimenta"
size="experimenta"
class="w-full"
>
<Button @click="confirmOrder" :disabled="isConfirming" variant="experimenta" size="experimenta"
class="w-full">
<span v-if="!isConfirming" class="flex items-center justify-center gap-2">
<svg
class="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>Jetzt verbindlich bestellen</span>
</span>
<span v-else class="flex items-center gap-2">
<div
class="animate-spin rounded-full h-4 w-4 border-2 border-white/20 border-t-white"
/>
<div class="animate-spin rounded-full h-4 w-4 border-2 border-white/20 border-t-white" />
Bestätigung läuft...
</span>
</Button>
@ -210,8 +184,8 @@ onMounted(() => {
<!-- Back Link -->
<div class="text-center pt-4">
<NuxtLink to="/checkout" class="text-sm text-experimenta-accent hover:underline">
Zurück zur Kasse
<NuxtLink to="/warenkorb" class="text-sm text-experimenta-accent hover:underline">
Zurück zum Warenkorb
</NuxtLink>
</div>
</div>

2
server/middleware/rate-limit.ts

@ -44,7 +44,7 @@ export default defineEventHandler((event) => {
// Define rate limits per endpoint
const limits: Record<string, { maxAttempts: number; windowMs: number }> = {
'/api/auth/login': { maxAttempts: 5, windowMs: 15 * 60 * 1000 }, // 5 per 15min
'/api/auth/login': { maxAttempts: 10, windowMs: 10 * 60 * 1000 }, // 10 per 10min
'/api/auth/register': { maxAttempts: 3, windowMs: 60 * 60 * 1000 }, // 3 per hour
}

Loading…
Cancel
Save