Switch entire UI to dark mode by default
Update all 26 Vue files (3 layouts, 4 components, 19 pages) and the Blade root template to use a dark color scheme: gray-950 backgrounds, gray-900 cards, gray-800 borders, light text, and adjusted status badges/flash messages for dark backgrounds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,7 @@ defineProps({
|
|||||||
:class="[
|
:class="[
|
||||||
'px-4 py-2 text-sm font-medium rounded-md disabled:opacity-50',
|
'px-4 py-2 text-sm font-medium rounded-md disabled:opacity-50',
|
||||||
variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
|
variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
|
||||||
variant === 'secondary' && 'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50',
|
variant === 'secondary' && 'bg-gray-800 text-gray-300 border border-gray-700 hover:bg-gray-700',
|
||||||
variant === 'danger' && 'bg-red-600 text-white hover:bg-red-700',
|
variant === 'danger' && 'bg-red-600 text-white hover:bg-red-700',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h3 v-if="title" class="text-sm font-medium text-gray-500 mb-2">{{ title }}</h3>
|
<h3 v-if="title" class="text-sm font-medium text-gray-400 mb-2">{{ title }}</h3>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ const flash = computed(() => page.props.flash || {});
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="flash.success" class="mb-4 rounded-md bg-green-50 p-4">
|
<div v-if="flash.success" class="mb-4 rounded-md bg-green-900/50 border border-green-800 p-4">
|
||||||
<p class="text-sm font-medium text-green-800">{{ flash.success }}</p>
|
<p class="text-sm font-medium text-green-300">{{ flash.success }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="flash.error" class="mb-4 rounded-md bg-red-50 p-4">
|
<div v-if="flash.error" class="mb-4 rounded-md bg-red-900/50 border border-red-800 p-4">
|
||||||
<p class="text-sm font-medium text-red-800">{{ flash.error }}</p>
|
<p class="text-sm font-medium text-red-300">{{ flash.error }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ defineProps({
|
|||||||
:class="[
|
:class="[
|
||||||
'px-3 py-2 rounded-md text-sm font-medium',
|
'px-3 py-2 rounded-md text-sm font-medium',
|
||||||
active
|
active
|
||||||
? 'bg-gray-100 text-gray-900'
|
? 'bg-gray-800 text-white'
|
||||||
: 'text-gray-700 hover:text-gray-900 hover:bg-gray-100',
|
: 'text-gray-300 hover:text-white hover:bg-gray-800',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -9,55 +9,55 @@ const domains = computed(() => page.props.domains);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen bg-gray-50">
|
<div class="min-h-screen bg-gray-950">
|
||||||
<nav class="bg-white border-b border-gray-200">
|
<nav class="bg-gray-900 border-b border-gray-800">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex justify-between h-16">
|
<div class="flex justify-between h-16">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<Link href="/dashboard" class="text-xl font-bold text-gray-900">
|
<Link href="/dashboard" class="text-xl font-bold text-white">
|
||||||
EZSCALE
|
EZSCALE
|
||||||
</Link>
|
</Link>
|
||||||
<div class="hidden sm:ml-8 sm:flex sm:space-x-4">
|
<div class="hidden sm:ml-8 sm:flex sm:space-x-4">
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard"
|
href="/dashboard"
|
||||||
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
class="px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-800"
|
||||||
>
|
>
|
||||||
Dashboard
|
Dashboard
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/subscriptions"
|
href="/subscriptions"
|
||||||
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
class="px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-800"
|
||||||
>
|
>
|
||||||
Subscriptions
|
Subscriptions
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/billing"
|
href="/billing"
|
||||||
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
class="px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-800"
|
||||||
>
|
>
|
||||||
Billing
|
Billing
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/plans"
|
href="/plans"
|
||||||
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
class="px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-800"
|
||||||
>
|
>
|
||||||
Plans
|
Plans
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/profile"
|
href="/profile"
|
||||||
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
class="px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-800"
|
||||||
>
|
>
|
||||||
Profile
|
Profile
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span v-if="user" class="text-sm text-gray-600">{{ user.name }}</span>
|
<span v-if="user" class="text-sm text-gray-400">{{ user.name }}</span>
|
||||||
<Link
|
<Link
|
||||||
v-if="user"
|
v-if="user"
|
||||||
href="/logout"
|
href="/logout"
|
||||||
method="post"
|
method="post"
|
||||||
as="button"
|
as="button"
|
||||||
class="text-sm text-gray-600 hover:text-gray-900"
|
class="text-sm text-gray-400 hover:text-white"
|
||||||
>
|
>
|
||||||
Log out
|
Log out
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ const marketingUrl = computed(() => `https://${domains.value?.marketing}`);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen flex flex-col items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
<div class="min-h-screen flex flex-col items-center justify-center bg-gray-950 py-12 px-4 sm:px-6 lg:px-8">
|
||||||
<div class="w-full max-w-md">
|
<div class="w-full max-w-md">
|
||||||
<div class="text-center mb-8">
|
<div class="text-center mb-8">
|
||||||
<a :href="marketingUrl" class="text-3xl font-bold text-gray-900">EZSCALE</a>
|
<a :href="marketingUrl" class="text-3xl font-bold text-white">EZSCALE</a>
|
||||||
<p class="mt-2 text-sm text-gray-600">Cloud Hosting Platform</p>
|
<p class="mt-2 text-sm text-gray-400">Cloud Hosting Platform</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white shadow-sm rounded-lg border border-gray-200 p-8">
|
<div class="bg-gray-900 shadow-sm rounded-lg border border-gray-800 p-8">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,27 +16,27 @@ const submit = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Confirm your password</h2>
|
<h2 class="text-xl font-semibold text-white mb-4">Confirm your password</h2>
|
||||||
<p class="text-sm text-gray-600 mb-6">Please confirm your password before continuing.</p>
|
<p class="text-sm text-gray-400 mb-6">Please confirm your password before continuing.</p>
|
||||||
|
|
||||||
<form @submit.prevent="submit" class="space-y-4">
|
<form @submit.prevent="submit" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
|
<label for="password" class="block text-sm font-medium text-gray-300">Password</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
v-model="form.password"
|
v-model="form.password"
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
autofocus
|
autofocus
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.password" class="mt-1 text-sm text-red-600">{{ form.errors.password }}</p>
|
<p v-if="form.errors.password" class="mt-1 text-sm text-red-400">{{ form.errors.password }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 focus:ring-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Confirm
|
Confirm
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -18,35 +18,35 @@ const submit = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Reset your password</h2>
|
<h2 class="text-xl font-semibold text-white mb-4">Reset your password</h2>
|
||||||
<p class="text-sm text-gray-600 mb-6">Enter your email and we'll send you a reset link.</p>
|
<p class="text-sm text-gray-400 mb-6">Enter your email and we'll send you a reset link.</p>
|
||||||
|
|
||||||
<div v-if="status" class="mb-4 text-sm font-medium text-green-600">{{ status }}</div>
|
<div v-if="status" class="mb-4 text-sm font-medium text-green-400">{{ status }}</div>
|
||||||
|
|
||||||
<form @submit.prevent="submit" class="space-y-4">
|
<form @submit.prevent="submit" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
|
<label for="email" class="block text-sm font-medium text-gray-300">Email</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
v-model="form.email"
|
v-model="form.email"
|
||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
autofocus
|
autofocus
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.email" class="mt-1 text-sm text-red-600">{{ form.errors.email }}</p>
|
<p v-if="form.errors.email" class="mt-1 text-sm text-red-400">{{ form.errors.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 focus:ring-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Send reset link
|
Send reset link
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p class="text-center text-sm text-gray-600">
|
<p class="text-center text-sm text-gray-400">
|
||||||
<a href="/login" class="text-blue-600 hover:text-blue-500">Back to login</a>
|
<a href="/login" class="text-blue-400 hover:text-blue-300">Back to login</a>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -18,52 +18,52 @@ const submit = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2 class="text-xl font-semibold text-gray-900 mb-6">Sign in to your account</h2>
|
<h2 class="text-xl font-semibold text-white mb-6">Sign in to your account</h2>
|
||||||
|
|
||||||
<form @submit.prevent="submit" class="space-y-4">
|
<form @submit.prevent="submit" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
|
<label for="email" class="block text-sm font-medium text-gray-300">Email</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
v-model="form.email"
|
v-model="form.email"
|
||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
autofocus
|
autofocus
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.email" class="mt-1 text-sm text-red-600">{{ form.errors.email }}</p>
|
<p v-if="form.errors.email" class="mt-1 text-sm text-red-400">{{ form.errors.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
|
<label for="password" class="block text-sm font-medium text-gray-300">Password</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
v-model="form.password"
|
v-model="form.password"
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.password" class="mt-1 text-sm text-red-600">{{ form.errors.password }}</p>
|
<p v-if="form.errors.password" class="mt-1 text-sm text-red-400">{{ form.errors.password }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<label class="flex items-center">
|
<label class="flex items-center">
|
||||||
<input v-model="form.remember" type="checkbox" class="rounded border-gray-300 text-blue-600" />
|
<input v-model="form.remember" type="checkbox" class="rounded bg-gray-800 border-gray-700 text-blue-600" />
|
||||||
<span class="ml-2 text-sm text-gray-600">Remember me</span>
|
<span class="ml-2 text-sm text-gray-400">Remember me</span>
|
||||||
</label>
|
</label>
|
||||||
<a href="/forgot-password" class="text-sm text-blue-600 hover:text-blue-500">Forgot password?</a>
|
<a href="/forgot-password" class="text-sm text-blue-400 hover:text-blue-300">Forgot password?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 focus:ring-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p class="text-center text-sm text-gray-600">
|
<p class="text-center text-sm text-gray-400">
|
||||||
Don't have an account? <a href="/register" class="text-blue-600 hover:text-blue-500">Sign up</a>
|
Don't have an account? <a href="/register" class="text-blue-400 hover:text-blue-300">Sign up</a>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -19,67 +19,67 @@ const submit = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2 class="text-xl font-semibold text-gray-900 mb-6">Create an account</h2>
|
<h2 class="text-xl font-semibold text-white mb-6">Create an account</h2>
|
||||||
|
|
||||||
<form @submit.prevent="submit" class="space-y-4">
|
<form @submit.prevent="submit" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
|
<label for="name" class="block text-sm font-medium text-gray-300">Name</label>
|
||||||
<input
|
<input
|
||||||
id="name"
|
id="name"
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
autofocus
|
autofocus
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.name" class="mt-1 text-sm text-red-600">{{ form.errors.name }}</p>
|
<p v-if="form.errors.name" class="mt-1 text-sm text-red-400">{{ form.errors.name }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
|
<label for="email" class="block text-sm font-medium text-gray-300">Email</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
v-model="form.email"
|
v-model="form.email"
|
||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.email" class="mt-1 text-sm text-red-600">{{ form.errors.email }}</p>
|
<p v-if="form.errors.email" class="mt-1 text-sm text-red-400">{{ form.errors.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
|
<label for="password" class="block text-sm font-medium text-gray-300">Password</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
v-model="form.password"
|
v-model="form.password"
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.password" class="mt-1 text-sm text-red-600">{{ form.errors.password }}</p>
|
<p v-if="form.errors.password" class="mt-1 text-sm text-red-400">{{ form.errors.password }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="password_confirmation" class="block text-sm font-medium text-gray-700">Confirm Password</label>
|
<label for="password_confirmation" class="block text-sm font-medium text-gray-300">Confirm Password</label>
|
||||||
<input
|
<input
|
||||||
id="password_confirmation"
|
id="password_confirmation"
|
||||||
v-model="form.password_confirmation"
|
v-model="form.password_confirmation"
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 focus:ring-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Create account
|
Create account
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p class="text-center text-sm text-gray-600">
|
<p class="text-center text-sm text-gray-400">
|
||||||
Already have an account? <a href="/login" class="text-blue-600 hover:text-blue-500">Sign in</a>
|
Already have an account? <a href="/login" class="text-blue-400 hover:text-blue-300">Sign in</a>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -24,48 +24,48 @@ const submit = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2 class="text-xl font-semibold text-gray-900 mb-6">Set new password</h2>
|
<h2 class="text-xl font-semibold text-white mb-6">Set new password</h2>
|
||||||
|
|
||||||
<form @submit.prevent="submit" class="space-y-4">
|
<form @submit.prevent="submit" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
|
<label for="email" class="block text-sm font-medium text-gray-300">Email</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
v-model="form.email"
|
v-model="form.email"
|
||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.email" class="mt-1 text-sm text-red-600">{{ form.errors.email }}</p>
|
<p v-if="form.errors.email" class="mt-1 text-sm text-red-400">{{ form.errors.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="password" class="block text-sm font-medium text-gray-700">New Password</label>
|
<label for="password" class="block text-sm font-medium text-gray-300">New Password</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
v-model="form.password"
|
v-model="form.password"
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.password" class="mt-1 text-sm text-red-600">{{ form.errors.password }}</p>
|
<p v-if="form.errors.password" class="mt-1 text-sm text-red-400">{{ form.errors.password }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="password_confirmation" class="block text-sm font-medium text-gray-700">Confirm Password</label>
|
<label for="password_confirmation" class="block text-sm font-medium text-gray-300">Confirm Password</label>
|
||||||
<input
|
<input
|
||||||
id="password_confirmation"
|
id="password_confirmation"
|
||||||
v-model="form.password_confirmation"
|
v-model="form.password_confirmation"
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 focus:ring-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Reset password
|
Reset password
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const toggleRecovery = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Two-Factor Authentication</h2>
|
<h2 class="text-xl font-semibold text-white mb-4">Two-Factor Authentication</h2>
|
||||||
<p class="text-sm text-gray-600 mb-6">
|
<p class="text-sm text-gray-400 mb-6">
|
||||||
<template v-if="!useRecovery">
|
<template v-if="!useRecovery">
|
||||||
Enter the authentication code from your authenticator app.
|
Enter the authentication code from your authenticator app.
|
||||||
</template>
|
</template>
|
||||||
@@ -37,7 +37,7 @@ const toggleRecovery = () => {
|
|||||||
|
|
||||||
<form @submit.prevent="submit" class="space-y-4">
|
<form @submit.prevent="submit" class="space-y-4">
|
||||||
<div v-if="!useRecovery">
|
<div v-if="!useRecovery">
|
||||||
<label for="code" class="block text-sm font-medium text-gray-700">Code</label>
|
<label for="code" class="block text-sm font-medium text-gray-300">Code</label>
|
||||||
<input
|
<input
|
||||||
id="code"
|
id="code"
|
||||||
v-model="form.code"
|
v-model="form.code"
|
||||||
@@ -45,27 +45,27 @@ const toggleRecovery = () => {
|
|||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
autofocus
|
autofocus
|
||||||
autocomplete="one-time-code"
|
autocomplete="one-time-code"
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.code" class="mt-1 text-sm text-red-600">{{ form.errors.code }}</p>
|
<p v-if="form.errors.code" class="mt-1 text-sm text-red-400">{{ form.errors.code }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<label for="recovery_code" class="block text-sm font-medium text-gray-700">Recovery Code</label>
|
<label for="recovery_code" class="block text-sm font-medium text-gray-300">Recovery Code</label>
|
||||||
<input
|
<input
|
||||||
id="recovery_code"
|
id="recovery_code"
|
||||||
v-model="form.recovery_code"
|
v-model="form.recovery_code"
|
||||||
type="text"
|
type="text"
|
||||||
autofocus
|
autofocus
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.recovery_code" class="mt-1 text-sm text-red-600">{{ form.errors.recovery_code }}</p>
|
<p v-if="form.errors.recovery_code" class="mt-1 text-sm text-red-400">{{ form.errors.recovery_code }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 focus:ring-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Verify
|
Verify
|
||||||
</button>
|
</button>
|
||||||
@@ -73,7 +73,7 @@ const toggleRecovery = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@click="toggleRecovery"
|
@click="toggleRecovery"
|
||||||
class="w-full text-center text-sm text-gray-600 hover:text-gray-900"
|
class="w-full text-center text-sm text-gray-400 hover:text-white"
|
||||||
>
|
>
|
||||||
{{ useRecovery ? 'Use authentication code' : 'Use a recovery code' }}
|
{{ useRecovery ? 'Use authentication code' : 'Use a recovery code' }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ const submit = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Verify your email</h2>
|
<h2 class="text-xl font-semibold text-white mb-4">Verify your email</h2>
|
||||||
<p class="text-sm text-gray-600 mb-6">
|
<p class="text-sm text-gray-400 mb-6">
|
||||||
We've sent a verification link to your email. Please check your inbox and click the link to verify.
|
We've sent a verification link to your email. Please check your inbox and click the link to verify.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="status === 'verification-link-sent'" class="mb-4 text-sm font-medium text-green-600">
|
<div v-if="status === 'verification-link-sent'" class="mb-4 text-sm font-medium text-green-400">
|
||||||
A new verification link has been sent to your email address.
|
A new verification link has been sent to your email address.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ const submit = () => {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 focus:ring-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Resend verification email
|
Resend verification email
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
|
||||||
import { useForm, Link } from '@inertiajs/vue3';
|
import { useForm, Link } from '@inertiajs/vue3';
|
||||||
import AppLayout from '@/Layouts/AppLayout.vue';
|
import AppLayout from '@/Layouts/AppLayout.vue';
|
||||||
|
|
||||||
@@ -13,10 +12,6 @@ defineProps({
|
|||||||
stripeKey: String,
|
stripeKey: String,
|
||||||
});
|
});
|
||||||
|
|
||||||
const addForm = useForm({
|
|
||||||
payment_method_id: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const defaultForm = useForm({
|
const defaultForm = useForm({
|
||||||
payment_method_id: '',
|
payment_method_id: '',
|
||||||
});
|
});
|
||||||
@@ -35,13 +30,13 @@ const removeMethod = (id) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Billing</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Billing</h1>
|
||||||
|
|
||||||
<div class="space-y-8">
|
<div class="space-y-8">
|
||||||
<!-- Payment Methods -->
|
<!-- Payment Methods -->
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h2 class="text-lg font-semibold text-gray-900">Payment Methods</h2>
|
<h2 class="text-lg font-semibold text-white">Payment Methods</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="paymentMethods.length === 0" class="text-sm text-gray-500">
|
<div v-if="paymentMethods.length === 0" class="text-sm text-gray-500">
|
||||||
@@ -53,26 +48,26 @@ const removeMethod = (id) => {
|
|||||||
v-for="pm in paymentMethods"
|
v-for="pm in paymentMethods"
|
||||||
:key="pm.id"
|
:key="pm.id"
|
||||||
class="flex items-center justify-between p-3 border rounded-md"
|
class="flex items-center justify-between p-3 border rounded-md"
|
||||||
:class="pm.is_default ? 'border-blue-300 bg-blue-50' : 'border-gray-200'"
|
:class="pm.is_default ? 'border-blue-700 bg-blue-900/20' : 'border-gray-700'"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="text-sm font-medium capitalize">{{ pm.brand }}</span>
|
<span class="text-sm font-medium text-gray-200 capitalize">{{ pm.brand }}</span>
|
||||||
<span class="text-sm text-gray-500">•••• {{ pm.last_four }}</span>
|
<span class="text-sm text-gray-500">•••• {{ pm.last_four }}</span>
|
||||||
<span class="text-sm text-gray-400">{{ pm.exp_month }}/{{ pm.exp_year }}</span>
|
<span class="text-sm text-gray-600">{{ pm.exp_month }}/{{ pm.exp_year }}</span>
|
||||||
<span v-if="pm.is_default" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">Default</span>
|
<span v-if="pm.is_default" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-900/50 text-blue-300 border border-blue-800">Default</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
v-if="!pm.is_default"
|
v-if="!pm.is_default"
|
||||||
@click="setDefault(pm.id)"
|
@click="setDefault(pm.id)"
|
||||||
:disabled="defaultForm.processing"
|
:disabled="defaultForm.processing"
|
||||||
class="text-sm text-blue-600 hover:text-blue-500"
|
class="text-sm text-blue-400 hover:text-blue-300"
|
||||||
>
|
>
|
||||||
Make Default
|
Make Default
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@click="removeMethod(pm.id)"
|
@click="removeMethod(pm.id)"
|
||||||
class="text-sm text-red-600 hover:text-red-500"
|
class="text-sm text-red-400 hover:text-red-300"
|
||||||
>
|
>
|
||||||
Remove
|
Remove
|
||||||
</button>
|
</button>
|
||||||
@@ -82,17 +77,17 @@ const removeMethod = (id) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Recent Invoices -->
|
<!-- Recent Invoices -->
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h2 class="text-lg font-semibold text-gray-900">Recent Invoices</h2>
|
<h2 class="text-lg font-semibold text-white">Recent Invoices</h2>
|
||||||
<Link href="/billing/invoices" class="text-sm text-blue-600 hover:text-blue-500">View All</Link>
|
<Link href="/billing/invoices" class="text-sm text-blue-400 hover:text-blue-300">View All</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="invoices.length === 0" class="text-sm text-gray-500">No invoices yet.</div>
|
<div v-if="invoices.length === 0" class="text-sm text-gray-500">No invoices yet.</div>
|
||||||
|
|
||||||
<table v-else class="w-full text-sm">
|
<table v-else class="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="border-b border-gray-200">
|
<tr class="border-b border-gray-800">
|
||||||
<th class="text-left py-2 text-gray-500 font-medium">Number</th>
|
<th class="text-left py-2 text-gray-500 font-medium">Number</th>
|
||||||
<th class="text-left py-2 text-gray-500 font-medium">Date</th>
|
<th class="text-left py-2 text-gray-500 font-medium">Date</th>
|
||||||
<th class="text-left py-2 text-gray-500 font-medium">Status</th>
|
<th class="text-left py-2 text-gray-500 font-medium">Status</th>
|
||||||
@@ -101,23 +96,23 @@ const removeMethod = (id) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="invoice in invoices" :key="invoice.id" class="border-b border-gray-100">
|
<tr v-for="invoice in invoices" :key="invoice.id" class="border-b border-gray-800/50">
|
||||||
<td class="py-2 text-gray-900">{{ invoice.number }}</td>
|
<td class="py-2 text-gray-200">{{ invoice.number }}</td>
|
||||||
<td class="py-2 text-gray-600">{{ new Date(invoice.created_at).toLocaleDateString() }}</td>
|
<td class="py-2 text-gray-400">{{ new Date(invoice.created_at).toLocaleDateString() }}</td>
|
||||||
<td class="py-2">
|
<td class="py-2">
|
||||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-green-100 text-green-800': invoice.status === 'paid',
|
'bg-green-900/50 text-green-300': invoice.status === 'paid',
|
||||||
'bg-yellow-100 text-yellow-800': invoice.status === 'pending',
|
'bg-yellow-900/50 text-yellow-300': invoice.status === 'pending',
|
||||||
'bg-red-100 text-red-800': invoice.status === 'overdue',
|
'bg-red-900/50 text-red-300': invoice.status === 'overdue',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ invoice.status }}
|
{{ invoice.status }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-2 text-right text-gray-900">${{ parseFloat(invoice.total).toFixed(2) }}</td>
|
<td class="py-2 text-right text-gray-200">${{ parseFloat(invoice.total).toFixed(2) }}</td>
|
||||||
<td class="py-2 text-right">
|
<td class="py-2 text-right">
|
||||||
<a :href="`/billing/invoices/${invoice.id}/download`" class="text-blue-600 hover:text-blue-500">Download</a>
|
<a :href="`/billing/invoices/${invoice.id}/download`" class="text-blue-400 hover:text-blue-300">Download</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -125,17 +120,17 @@ const removeMethod = (id) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Recent Transactions -->
|
<!-- Recent Transactions -->
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h2 class="text-lg font-semibold text-gray-900">Recent Transactions</h2>
|
<h2 class="text-lg font-semibold text-white">Recent Transactions</h2>
|
||||||
<Link href="/billing/transactions" class="text-sm text-blue-600 hover:text-blue-500">View All</Link>
|
<Link href="/billing/transactions" class="text-sm text-blue-400 hover:text-blue-300">View All</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="transactions.length === 0" class="text-sm text-gray-500">No transactions yet.</div>
|
<div v-if="transactions.length === 0" class="text-sm text-gray-500">No transactions yet.</div>
|
||||||
|
|
||||||
<table v-else class="w-full text-sm">
|
<table v-else class="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="border-b border-gray-200">
|
<tr class="border-b border-gray-800">
|
||||||
<th class="text-left py-2 text-gray-500 font-medium">Date</th>
|
<th class="text-left py-2 text-gray-500 font-medium">Date</th>
|
||||||
<th class="text-left py-2 text-gray-500 font-medium">Gateway</th>
|
<th class="text-left py-2 text-gray-500 font-medium">Gateway</th>
|
||||||
<th class="text-left py-2 text-gray-500 font-medium">Status</th>
|
<th class="text-left py-2 text-gray-500 font-medium">Status</th>
|
||||||
@@ -144,22 +139,22 @@ const removeMethod = (id) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="tx in transactions" :key="tx.id" class="border-b border-gray-100">
|
<tr v-for="tx in transactions" :key="tx.id" class="border-b border-gray-800/50">
|
||||||
<td class="py-2 text-gray-600">{{ new Date(tx.created_at).toLocaleDateString() }}</td>
|
<td class="py-2 text-gray-400">{{ new Date(tx.created_at).toLocaleDateString() }}</td>
|
||||||
<td class="py-2 text-gray-600 capitalize">{{ tx.gateway }}</td>
|
<td class="py-2 text-gray-400 capitalize">{{ tx.gateway }}</td>
|
||||||
<td class="py-2">
|
<td class="py-2">
|
||||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-green-100 text-green-800': tx.status === 'succeeded',
|
'bg-green-900/50 text-green-300': tx.status === 'succeeded',
|
||||||
'bg-red-100 text-red-800': tx.status === 'failed',
|
'bg-red-900/50 text-red-300': tx.status === 'failed',
|
||||||
'bg-yellow-100 text-yellow-800': tx.status === 'pending',
|
'bg-yellow-900/50 text-yellow-300': tx.status === 'pending',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ tx.status }}
|
{{ tx.status }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-2 text-gray-600">{{ tx.description }}</td>
|
<td class="py-2 text-gray-400">{{ tx.description }}</td>
|
||||||
<td class="py-2 text-right text-gray-900">${{ parseFloat(tx.amount).toFixed(2) }}</td>
|
<td class="py-2 text-right text-gray-200">${{ parseFloat(tx.amount).toFixed(2) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -12,18 +12,18 @@ defineProps({
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<Link href="/billing" class="text-sm text-blue-600 hover:text-blue-500">← Back to Billing</Link>
|
<Link href="/billing" class="text-sm text-blue-400 hover:text-blue-300">← Back to Billing</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Invoices</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Invoices</h1>
|
||||||
|
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm overflow-hidden">
|
||||||
<div v-if="!invoices.data || invoices.data.length === 0" class="p-6 text-sm text-gray-500 text-center">
|
<div v-if="!invoices.data || invoices.data.length === 0" class="p-6 text-sm text-gray-500 text-center">
|
||||||
No invoices found.
|
No invoices found.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table v-else class="w-full text-sm">
|
<table v-else class="w-full text-sm">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-800/50">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-left px-6 py-3 text-gray-500 font-medium">Number</th>
|
<th class="text-left px-6 py-3 text-gray-500 font-medium">Number</th>
|
||||||
<th class="text-left px-6 py-3 text-gray-500 font-medium">Date</th>
|
<th class="text-left px-6 py-3 text-gray-500 font-medium">Date</th>
|
||||||
@@ -34,31 +34,31 @@ defineProps({
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="invoice in invoices.data" :key="invoice.id" class="border-t border-gray-100">
|
<tr v-for="invoice in invoices.data" :key="invoice.id" class="border-t border-gray-800">
|
||||||
<td class="px-6 py-3 text-gray-900">{{ invoice.number }}</td>
|
<td class="px-6 py-3 text-gray-200">{{ invoice.number }}</td>
|
||||||
<td class="px-6 py-3 text-gray-600">{{ new Date(invoice.created_at).toLocaleDateString() }}</td>
|
<td class="px-6 py-3 text-gray-400">{{ new Date(invoice.created_at).toLocaleDateString() }}</td>
|
||||||
<td class="px-6 py-3 text-gray-600 capitalize">{{ invoice.gateway }}</td>
|
<td class="px-6 py-3 text-gray-400 capitalize">{{ invoice.gateway }}</td>
|
||||||
<td class="px-6 py-3">
|
<td class="px-6 py-3">
|
||||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-green-100 text-green-800': invoice.status === 'paid',
|
'bg-green-900/50 text-green-300': invoice.status === 'paid',
|
||||||
'bg-yellow-100 text-yellow-800': invoice.status === 'pending',
|
'bg-yellow-900/50 text-yellow-300': invoice.status === 'pending',
|
||||||
'bg-red-100 text-red-800': invoice.status === 'overdue',
|
'bg-red-900/50 text-red-300': invoice.status === 'overdue',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ invoice.status }}
|
{{ invoice.status }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-3 text-right text-gray-900">${{ parseFloat(invoice.total).toFixed(2) }}</td>
|
<td class="px-6 py-3 text-right text-gray-200">${{ parseFloat(invoice.total).toFixed(2) }}</td>
|
||||||
<td class="px-6 py-3 text-right">
|
<td class="px-6 py-3 text-right">
|
||||||
<a :href="`/billing/invoices/${invoice.id}/download`" class="text-blue-600 hover:text-blue-500">Download</a>
|
<a :href="`/billing/invoices/${invoice.id}/download`" class="text-blue-400 hover:text-blue-300">Download</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
<div v-if="invoices.links && invoices.last_page > 1" class="px-6 py-3 border-t border-gray-200 flex items-center justify-between">
|
<div v-if="invoices.links && invoices.last_page > 1" class="px-6 py-3 border-t border-gray-800 flex items-center justify-between">
|
||||||
<div class="text-sm text-gray-500">
|
<div class="text-sm text-gray-500">
|
||||||
Showing {{ invoices.from }} to {{ invoices.to }} of {{ invoices.total }}
|
Showing {{ invoices.from }} to {{ invoices.to }} of {{ invoices.total }}
|
||||||
</div>
|
</div>
|
||||||
@@ -69,7 +69,7 @@ defineProps({
|
|||||||
:href="link.url || '#'"
|
:href="link.url || '#'"
|
||||||
:class="[
|
:class="[
|
||||||
'px-3 py-1 text-sm rounded',
|
'px-3 py-1 text-sm rounded',
|
||||||
link.active ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100',
|
link.active ? 'bg-blue-600 text-white' : 'text-gray-400 hover:bg-gray-800',
|
||||||
!link.url && 'opacity-50 pointer-events-none',
|
!link.url && 'opacity-50 pointer-events-none',
|
||||||
]"
|
]"
|
||||||
v-html="link.label"
|
v-html="link.label"
|
||||||
|
|||||||
@@ -12,18 +12,18 @@ defineProps({
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<Link href="/billing" class="text-sm text-blue-600 hover:text-blue-500">← Back to Billing</Link>
|
<Link href="/billing" class="text-sm text-blue-400 hover:text-blue-300">← Back to Billing</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Transactions</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Transactions</h1>
|
||||||
|
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm overflow-hidden">
|
||||||
<div v-if="!transactions.data || transactions.data.length === 0" class="p-6 text-sm text-gray-500 text-center">
|
<div v-if="!transactions.data || transactions.data.length === 0" class="p-6 text-sm text-gray-500 text-center">
|
||||||
No transactions found.
|
No transactions found.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table v-else class="w-full text-sm">
|
<table v-else class="w-full text-sm">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-800/50">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-left px-6 py-3 text-gray-500 font-medium">Date</th>
|
<th class="text-left px-6 py-3 text-gray-500 font-medium">Date</th>
|
||||||
<th class="text-left px-6 py-3 text-gray-500 font-medium">Gateway</th>
|
<th class="text-left px-6 py-3 text-gray-500 font-medium">Gateway</th>
|
||||||
@@ -34,30 +34,30 @@ defineProps({
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="tx in transactions.data" :key="tx.id" class="border-t border-gray-100">
|
<tr v-for="tx in transactions.data" :key="tx.id" class="border-t border-gray-800">
|
||||||
<td class="px-6 py-3 text-gray-600">{{ new Date(tx.created_at).toLocaleDateString() }}</td>
|
<td class="px-6 py-3 text-gray-400">{{ new Date(tx.created_at).toLocaleDateString() }}</td>
|
||||||
<td class="px-6 py-3 text-gray-600 capitalize">{{ tx.gateway }}</td>
|
<td class="px-6 py-3 text-gray-400 capitalize">{{ tx.gateway }}</td>
|
||||||
<td class="px-6 py-3 text-gray-600 capitalize">{{ tx.payment_method }}</td>
|
<td class="px-6 py-3 text-gray-400 capitalize">{{ tx.payment_method }}</td>
|
||||||
<td class="px-6 py-3">
|
<td class="px-6 py-3">
|
||||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium capitalize"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-green-100 text-green-800': tx.status === 'succeeded',
|
'bg-green-900/50 text-green-300': tx.status === 'succeeded',
|
||||||
'bg-red-100 text-red-800': tx.status === 'failed',
|
'bg-red-900/50 text-red-300': tx.status === 'failed',
|
||||||
'bg-yellow-100 text-yellow-800': tx.status === 'pending',
|
'bg-yellow-900/50 text-yellow-300': tx.status === 'pending',
|
||||||
'bg-gray-100 text-gray-800': tx.status === 'refunded',
|
'bg-gray-800 text-gray-400': tx.status === 'refunded',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ tx.status }}
|
{{ tx.status }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-3 text-gray-600">{{ tx.description }}</td>
|
<td class="px-6 py-3 text-gray-400">{{ tx.description }}</td>
|
||||||
<td class="px-6 py-3 text-right text-gray-900">${{ parseFloat(tx.amount).toFixed(2) }}</td>
|
<td class="px-6 py-3 text-right text-gray-200">${{ parseFloat(tx.amount).toFixed(2) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
<div v-if="transactions.links && transactions.last_page > 1" class="px-6 py-3 border-t border-gray-200 flex items-center justify-between">
|
<div v-if="transactions.links && transactions.last_page > 1" class="px-6 py-3 border-t border-gray-800 flex items-center justify-between">
|
||||||
<div class="text-sm text-gray-500">
|
<div class="text-sm text-gray-500">
|
||||||
Showing {{ transactions.from }} to {{ transactions.to }} of {{ transactions.total }}
|
Showing {{ transactions.from }} to {{ transactions.to }} of {{ transactions.total }}
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +68,7 @@ defineProps({
|
|||||||
:href="link.url || '#'"
|
:href="link.url || '#'"
|
||||||
:class="[
|
:class="[
|
||||||
'px-3 py-1 text-sm rounded',
|
'px-3 py-1 text-sm rounded',
|
||||||
link.active ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100',
|
link.active ? 'bg-blue-600 text-white' : 'text-gray-400 hover:bg-gray-800',
|
||||||
!link.url && 'opacity-50 pointer-events-none',
|
!link.url && 'opacity-50 pointer-events-none',
|
||||||
]"
|
]"
|
||||||
v-html="link.label"
|
v-html="link.label"
|
||||||
|
|||||||
@@ -73,32 +73,32 @@ const submit = () => {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<Link href="/plans" class="text-sm text-blue-600 hover:text-blue-500">← Back to Plans</Link>
|
<Link href="/plans" class="text-sm text-blue-400 hover:text-blue-300">← Back to Plans</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Checkout</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Checkout</h1>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
<!-- Order Summary -->
|
<!-- Order Summary -->
|
||||||
<div class="lg:col-span-1 order-2 lg:order-1">
|
<div class="lg:col-span-1 order-2 lg:order-1">
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">Order Summary</h2>
|
<h2 class="text-lg font-semibold text-white mb-4">Order Summary</h2>
|
||||||
|
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-gray-600">{{ plan.name }}</span>
|
<span class="text-gray-400">{{ plan.name }}</span>
|
||||||
<span class="text-gray-900">${{ parseFloat(plan.price).toFixed(2) }}</span>
|
<span class="text-white">${{ parseFloat(plan.price).toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm text-gray-500">
|
<div class="flex justify-between text-sm text-gray-500">
|
||||||
<span>Billing Cycle</span>
|
<span>Billing Cycle</span>
|
||||||
<span class="capitalize">{{ plan.billing_cycle }}</span>
|
<span class="capitalize">{{ plan.billing_cycle }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="couponApplied" class="flex justify-between text-sm text-green-600">
|
<div v-if="couponApplied" class="flex justify-between text-sm text-green-400">
|
||||||
<span>Discount</span>
|
<span>Discount</span>
|
||||||
<span>-${{ couponDiscount.toFixed(2) }}</span>
|
<span>-${{ couponDiscount.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<hr class="border-gray-200">
|
<hr class="border-gray-800">
|
||||||
<div class="flex justify-between font-semibold">
|
<div class="flex justify-between font-semibold text-white">
|
||||||
<span>Total</span>
|
<span>Total</span>
|
||||||
<span>${{ total }}/{{ plan.billing_cycle }}</span>
|
<span>${{ total }}/{{ plan.billing_cycle }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,24 +110,24 @@ const submit = () => {
|
|||||||
<div class="lg:col-span-2 order-1 lg:order-2">
|
<div class="lg:col-span-2 order-1 lg:order-2">
|
||||||
<form @submit.prevent="submit" class="space-y-6">
|
<form @submit.prevent="submit" class="space-y-6">
|
||||||
<!-- Payment Gateway -->
|
<!-- Payment Gateway -->
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">Payment Method</h2>
|
<h2 class="text-lg font-semibold text-white mb-4">Payment Method</h2>
|
||||||
|
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<label class="flex items-center p-3 border rounded-md cursor-pointer" :class="selectedGateway === 'stripe' ? 'border-blue-500 bg-blue-50' : 'border-gray-200'">
|
<label class="flex items-center p-3 border rounded-md cursor-pointer" :class="selectedGateway === 'stripe' ? 'border-blue-500 bg-blue-900/20' : 'border-gray-700'">
|
||||||
<input v-model="selectedGateway" type="radio" value="stripe" class="mr-3">
|
<input v-model="selectedGateway" type="radio" value="stripe" class="mr-3">
|
||||||
<span class="text-sm font-medium">Credit / Debit Card (Stripe)</span>
|
<span class="text-sm font-medium text-gray-200">Credit / Debit Card (Stripe)</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center p-3 border rounded-md cursor-pointer" :class="selectedGateway === 'paypal' ? 'border-blue-500 bg-blue-50' : 'border-gray-200'">
|
<label class="flex items-center p-3 border rounded-md cursor-pointer" :class="selectedGateway === 'paypal' ? 'border-blue-500 bg-blue-900/20' : 'border-gray-700'">
|
||||||
<input v-model="selectedGateway" type="radio" value="paypal" class="mr-3">
|
<input v-model="selectedGateway" type="radio" value="paypal" class="mr-3">
|
||||||
<span class="text-sm font-medium">PayPal</span>
|
<span class="text-sm font-medium text-gray-200">PayPal</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Saved Payment Methods (Stripe) -->
|
<!-- Saved Payment Methods (Stripe) -->
|
||||||
<div v-if="selectedGateway === 'stripe' && paymentMethods.length > 0" class="mt-4">
|
<div v-if="selectedGateway === 'stripe' && paymentMethods.length > 0" class="mt-4">
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">Select Card</label>
|
<label class="block text-sm font-medium text-gray-300 mb-2">Select Card</label>
|
||||||
<select v-model="selectedPaymentMethod" class="w-full rounded-md border-gray-300 text-sm">
|
<select v-model="selectedPaymentMethod" class="w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 text-sm">
|
||||||
<option v-for="pm in paymentMethods" :key="pm.id" :value="pm.id">
|
<option v-for="pm in paymentMethods" :key="pm.id" :value="pm.id">
|
||||||
{{ pm.brand }} ending in {{ pm.last_four }} ({{ pm.exp_month }}/{{ pm.exp_year }})
|
{{ pm.brand }} ending in {{ pm.last_four }} ({{ pm.exp_month }}/{{ pm.exp_year }})
|
||||||
<template v-if="pm.is_default"> - Default</template>
|
<template v-if="pm.is_default"> - Default</template>
|
||||||
@@ -138,39 +138,39 @@ const submit = () => {
|
|||||||
<div v-if="selectedGateway === 'stripe' && paymentMethods.length === 0" class="mt-4">
|
<div v-if="selectedGateway === 'stripe' && paymentMethods.length === 0" class="mt-4">
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
You have no saved payment methods.
|
You have no saved payment methods.
|
||||||
<Link href="/billing" class="text-blue-600 hover:text-blue-500">Add one first</Link>.
|
<Link href="/billing" class="text-blue-400 hover:text-blue-300">Add one first</Link>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Coupon -->
|
<!-- Coupon -->
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">Coupon Code</h2>
|
<h2 class="text-lg font-semibold text-white mb-4">Coupon Code</h2>
|
||||||
|
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<input
|
<input
|
||||||
v-model="couponCode"
|
v-model="couponCode"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter coupon code"
|
placeholder="Enter coupon code"
|
||||||
class="flex-1 rounded-md border-gray-300 text-sm"
|
class="flex-1 rounded-md bg-gray-800 border-gray-700 text-gray-100 text-sm placeholder-gray-500"
|
||||||
:disabled="couponApplied"
|
:disabled="couponApplied"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@click="applyCoupon"
|
@click="applyCoupon"
|
||||||
:disabled="!couponCode || couponApplied"
|
:disabled="!couponCode || couponApplied"
|
||||||
class="px-4 py-2 bg-gray-100 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-200 disabled:opacity-50"
|
class="px-4 py-2 bg-gray-800 text-sm font-medium text-gray-300 rounded-md hover:bg-gray-700 border border-gray-700 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{{ couponApplied ? 'Applied' : 'Apply' }}
|
{{ couponApplied ? 'Applied' : 'Apply' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="couponError" class="mt-2 text-sm text-red-600">{{ couponError }}</p>
|
<p v-if="couponError" class="mt-2 text-sm text-red-400">{{ couponError }}</p>
|
||||||
<p v-if="couponApplied" class="mt-2 text-sm text-green-600">Coupon applied successfully!</p>
|
<p v-if="couponApplied" class="mt-2 text-sm text-green-400">Coupon applied successfully!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Errors -->
|
<!-- Errors -->
|
||||||
<div v-if="form.errors && Object.keys(form.errors).length" class="rounded-md bg-red-50 p-4">
|
<div v-if="form.errors && Object.keys(form.errors).length" class="rounded-md bg-red-900/50 border border-red-800 p-4">
|
||||||
<ul class="list-disc list-inside text-sm text-red-600">
|
<ul class="list-disc list-inside text-sm text-red-300">
|
||||||
<li v-for="(error, field) in form.errors" :key="field">{{ error }}</li>
|
<li v-for="(error, field) in form.errors" :key="field">{{ error }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,26 +12,26 @@ defineProps({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Dashboard</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Dashboard</h1>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h3 class="text-sm font-medium text-gray-500">Total Services</h3>
|
<h3 class="text-sm font-medium text-gray-400">Total Services</h3>
|
||||||
<p class="mt-2 text-3xl font-bold text-gray-900">{{ servicesCount }}</p>
|
<p class="mt-2 text-3xl font-bold text-white">{{ servicesCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h3 class="text-sm font-medium text-gray-500">Active Services</h3>
|
<h3 class="text-sm font-medium text-gray-400">Active Services</h3>
|
||||||
<p class="mt-2 text-3xl font-bold text-green-600">{{ activeServicesCount }}</p>
|
<p class="mt-2 text-3xl font-bold text-green-400">{{ activeServicesCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h3 class="text-sm font-medium text-gray-500">Quick Actions</h3>
|
<h3 class="text-sm font-medium text-gray-400">Quick Actions</h3>
|
||||||
<div class="mt-3 space-y-2">
|
<div class="mt-3 space-y-2">
|
||||||
<Link href="/plans" class="block text-sm text-blue-600 hover:text-blue-500">Browse Plans</Link>
|
<Link href="/plans" class="block text-sm text-blue-400 hover:text-blue-300">Browse Plans</Link>
|
||||||
<Link href="/subscriptions" class="block text-sm text-blue-600 hover:text-blue-500">My Subscriptions</Link>
|
<Link href="/subscriptions" class="block text-sm text-blue-400 hover:text-blue-300">My Subscriptions</Link>
|
||||||
<Link href="/billing" class="block text-sm text-blue-600 hover:text-blue-500">Billing & Payments</Link>
|
<Link href="/billing" class="block text-sm text-blue-400 hover:text-blue-300">Billing & Payments</Link>
|
||||||
<Link href="/profile" class="block text-sm text-blue-600 hover:text-blue-500">Edit Profile</Link>
|
<Link href="/profile" class="block text-sm text-blue-400 hover:text-blue-300">Edit Profile</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ const accountUrl = computed(() => `https://${domains.value?.account}`);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen bg-white">
|
<div class="min-h-screen bg-gray-950">
|
||||||
<nav class="border-b border-gray-200">
|
<nav class="border-b border-gray-800">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex justify-between h-16 items-center">
|
<div class="flex justify-between h-16 items-center">
|
||||||
<span class="text-xl font-bold text-gray-900">EZSCALE</span>
|
<span class="text-xl font-bold text-white">EZSCALE</span>
|
||||||
<div class="space-x-4">
|
<div class="space-x-4">
|
||||||
<a :href="accountUrl + '/login'" class="text-sm text-gray-600 hover:text-gray-900">Sign in</a>
|
<a :href="accountUrl + '/login'" class="text-sm text-gray-400 hover:text-white">Sign in</a>
|
||||||
<a :href="accountUrl + '/register'" class="text-sm px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">Get Started</a>
|
<a :href="accountUrl + '/register'" class="text-sm px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">Get Started</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,11 +23,11 @@ const accountUrl = computed(() => `https://${domains.value?.account}`);
|
|||||||
|
|
||||||
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24">
|
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="text-4xl font-bold text-gray-900 sm:text-5xl md:text-6xl">
|
<h1 class="text-4xl font-bold text-white sm:text-5xl md:text-6xl">
|
||||||
Cloud Hosting
|
Cloud Hosting
|
||||||
<span class="text-blue-600">Made Simple</span>
|
<span class="text-blue-400">Made Simple</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mt-6 max-w-2xl mx-auto text-xl text-gray-500">
|
<p class="mt-6 max-w-2xl mx-auto text-xl text-gray-400">
|
||||||
VPS, Dedicated Servers, Web Hosting, and Game Servers. Powered by EZSCALE.
|
VPS, Dedicated Servers, Web Hosting, and Game Servers. Powered by EZSCALE.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-10">
|
<div class="mt-10">
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ const serviceTypeLabels = {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Plans & Pricing</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Plans & Pricing</h1>
|
||||||
|
|
||||||
<div v-for="(plans, type) in plansByType" :key="type" class="mb-10">
|
<div v-for="(plans, type) in plansByType" :key="type" class="mb-10">
|
||||||
<h2 class="text-xl font-semibold text-gray-800 mb-4">
|
<h2 class="text-xl font-semibold text-gray-200 mb-4">
|
||||||
{{ serviceTypeLabels[type] || type }}
|
{{ serviceTypeLabels[type] || type }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
@@ -34,28 +34,28 @@ const serviceTypeLabels = {
|
|||||||
<div
|
<div
|
||||||
v-for="plan in plans"
|
v-for="plan in plans"
|
||||||
:key="plan.id"
|
:key="plan.id"
|
||||||
class="bg-white rounded-lg border border-gray-200 shadow-sm p-6 flex flex-col"
|
class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6 flex flex-col"
|
||||||
>
|
>
|
||||||
<h3 class="text-lg font-semibold text-gray-900">{{ plan.name }}</h3>
|
<h3 class="text-lg font-semibold text-white">{{ plan.name }}</h3>
|
||||||
<p v-if="plan.description" class="mt-1 text-sm text-gray-500">{{ plan.description }}</p>
|
<p v-if="plan.description" class="mt-1 text-sm text-gray-400">{{ plan.description }}</p>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<span class="text-3xl font-bold text-gray-900">
|
<span class="text-3xl font-bold text-white">
|
||||||
{{ formatPrice(plan.price, plan.billing_cycle) }}
|
{{ formatPrice(plan.price, plan.billing_cycle) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul v-if="plan.features" class="mt-4 space-y-2 flex-1">
|
<ul v-if="plan.features" class="mt-4 space-y-2 flex-1">
|
||||||
<li v-for="(value, feature) in plan.features" :key="feature" class="flex items-start text-sm text-gray-600">
|
<li v-for="(value, feature) in plan.features" :key="feature" class="flex items-start text-sm text-gray-400">
|
||||||
<svg class="h-5 w-5 text-green-500 mr-2 shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
<svg class="h-5 w-5 text-green-400 mr-2 shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
<span><strong>{{ feature }}:</strong> {{ value }}</span>
|
<span><strong class="text-gray-300">{{ feature }}:</strong> {{ value }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<span v-if="plan.stock_quantity !== null && plan.stock_quantity <= 0" class="block text-center text-sm font-medium text-red-600">
|
<span v-if="plan.stock_quantity !== null && plan.stock_quantity <= 0" class="block text-center text-sm font-medium text-red-400">
|
||||||
Out of Stock
|
Out of Stock
|
||||||
</span>
|
</span>
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -17,33 +17,33 @@ const formatPrice = (price, cycle) => {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<Link href="/plans" class="text-sm text-blue-600 hover:text-blue-500">← Back to Plans</Link>
|
<Link href="/plans" class="text-sm text-blue-400 hover:text-blue-300">← Back to Plans</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-8 max-w-2xl">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-8 max-w-2xl">
|
||||||
<h1 class="text-2xl font-bold text-gray-900">{{ plan.name }}</h1>
|
<h1 class="text-2xl font-bold text-white">{{ plan.name }}</h1>
|
||||||
<p v-if="plan.description" class="mt-2 text-gray-500">{{ plan.description }}</p>
|
<p v-if="plan.description" class="mt-2 text-gray-400">{{ plan.description }}</p>
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<span class="text-4xl font-bold text-gray-900">
|
<span class="text-4xl font-bold text-white">
|
||||||
{{ formatPrice(plan.price, plan.billing_cycle) }}
|
{{ formatPrice(plan.price, plan.billing_cycle) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="plan.features" class="mt-8">
|
<div v-if="plan.features" class="mt-8">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-3">Features</h2>
|
<h2 class="text-lg font-semibold text-white mb-3">Features</h2>
|
||||||
<ul class="space-y-2">
|
<ul class="space-y-2">
|
||||||
<li v-for="(value, feature) in plan.features" :key="feature" class="flex items-start text-sm text-gray-600">
|
<li v-for="(value, feature) in plan.features" :key="feature" class="flex items-start text-sm text-gray-400">
|
||||||
<svg class="h-5 w-5 text-green-500 mr-2 shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
<svg class="h-5 w-5 text-green-400 mr-2 shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
<span><strong>{{ feature }}:</strong> {{ value }}</span>
|
<span><strong class="text-gray-300">{{ feature }}:</strong> {{ value }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<span v-if="plan.stock_quantity !== null && plan.stock_quantity <= 0" class="block text-center text-sm font-medium text-red-600">
|
<span v-if="plan.stock_quantity !== null && plan.stock_quantity <= 0" class="block text-center text-sm font-medium text-red-400">
|
||||||
This plan is currently out of stock.
|
This plan is currently out of stock.
|
||||||
</span>
|
</span>
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -21,49 +21,49 @@ const submit = () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="max-w-2xl">
|
<div class="max-w-2xl">
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Profile Settings</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Profile Settings</h1>
|
||||||
|
|
||||||
<form @submit.prevent="submit" class="bg-white rounded-lg border border-gray-200 shadow-sm p-6 space-y-4">
|
<form @submit.prevent="submit" class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6 space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
|
<label for="name" class="block text-sm font-medium text-gray-300">Name</label>
|
||||||
<input
|
<input
|
||||||
id="name"
|
id="name"
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="form.errors.name" class="mt-1 text-sm text-red-600">{{ form.errors.name }}</p>
|
<p v-if="form.errors.name" class="mt-1 text-sm text-red-400">{{ form.errors.name }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
|
<label for="email" class="block text-sm font-medium text-gray-300">Email</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
:value="user.email"
|
:value="user.email"
|
||||||
type="email"
|
type="email"
|
||||||
disabled
|
disabled
|
||||||
class="mt-1 block w-full rounded-md border-gray-200 bg-gray-50 shadow-sm px-3 py-2 border text-gray-500"
|
class="mt-1 block w-full rounded-md bg-gray-800/50 border-gray-700 shadow-sm px-3 py-2 border text-gray-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="phone" class="block text-sm font-medium text-gray-700">Phone</label>
|
<label for="phone" class="block text-sm font-medium text-gray-300">Phone</label>
|
||||||
<input
|
<input
|
||||||
id="phone"
|
id="phone"
|
||||||
v-model="form.phone"
|
v-model="form.phone"
|
||||||
type="tel"
|
type="tel"
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="company" class="block text-sm font-medium text-gray-700">Company</label>
|
<label for="company" class="block text-sm font-medium text-gray-300">Company</label>
|
||||||
<input
|
<input
|
||||||
id="company"
|
id="company"
|
||||||
v-model="form.company"
|
v-model="form.company"
|
||||||
type="text"
|
type="text"
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ const disableTwoFactor = () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="max-w-2xl">
|
<div class="max-w-2xl">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">Two-Factor Authentication</h2>
|
<h2 class="text-lg font-semibold text-white mb-4">Two-Factor Authentication</h2>
|
||||||
<p class="text-sm text-gray-600 mb-6">
|
<p class="text-sm text-gray-400 mb-6">
|
||||||
Add an extra layer of security to your account using a TOTP authenticator app.
|
Add an extra layer of security to your account using a TOTP authenticator app.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -85,22 +85,22 @@ const disableTwoFactor = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="confirming" class="mt-6">
|
<div v-if="confirming" class="mt-6">
|
||||||
<p class="text-sm text-gray-600 mb-4">
|
<p class="text-sm text-gray-400 mb-4">
|
||||||
Scan this QR code with your authenticator app, then enter the code below to confirm.
|
Scan this QR code with your authenticator app, then enter the code below to confirm.
|
||||||
</p>
|
</p>
|
||||||
<div v-if="qrCode" v-html="qrCode" class="mb-4"></div>
|
<div v-if="qrCode" v-html="qrCode" class="mb-4 [&_svg]:fill-white"></div>
|
||||||
|
|
||||||
<form @submit.prevent="confirmTwoFactor" class="space-y-4 max-w-xs">
|
<form @submit.prevent="confirmTwoFactor" class="space-y-4 max-w-xs">
|
||||||
<div>
|
<div>
|
||||||
<label for="code" class="block text-sm font-medium text-gray-700">Confirmation Code</label>
|
<label for="code" class="block text-sm font-medium text-gray-300">Confirmation Code</label>
|
||||||
<input
|
<input
|
||||||
id="code"
|
id="code"
|
||||||
v-model="confirmationForm.code"
|
v-model="confirmationForm.code"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
class="mt-1 block w-full rounded-md bg-gray-800 border-gray-700 text-gray-100 shadow-sm focus:border-blue-500 focus:ring-blue-500 px-3 py-2 border"
|
||||||
/>
|
/>
|
||||||
<p v-if="confirmationForm.errors.code" class="mt-1 text-sm text-red-600">{{ confirmationForm.errors.code }}</p>
|
<p v-if="confirmationForm.errors.code" class="mt-1 text-sm text-red-400">{{ confirmationForm.errors.code }}</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -113,15 +113,15 @@ const disableTwoFactor = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="recoveryCodes.length > 0 && !confirming" class="mt-6">
|
<div v-if="recoveryCodes.length > 0 && !confirming" class="mt-6">
|
||||||
<h3 class="text-sm font-semibold text-gray-900 mb-2">Recovery Codes</h3>
|
<h3 class="text-sm font-semibold text-white mb-2">Recovery Codes</h3>
|
||||||
<p class="text-sm text-gray-600 mb-3">Store these codes in a safe place. They can be used to access your account if you lose your authenticator device.</p>
|
<p class="text-sm text-gray-400 mb-3">Store these codes in a safe place. They can be used to access your account if you lose your authenticator device.</p>
|
||||||
<div class="bg-gray-100 rounded-md p-4 font-mono text-sm">
|
<div class="bg-gray-800 rounded-md p-4 font-mono text-sm text-gray-300">
|
||||||
<div v-for="code in recoveryCodes" :key="code">{{ code }}</div>
|
<div v-for="code in recoveryCodes" :key="code">{{ code }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="page.props.auth?.user?.two_factor_enabled && !confirming" class="mt-6">
|
<div v-if="page.props.auth?.user?.two_factor_enabled && !confirming" class="mt-6">
|
||||||
<p class="text-sm text-green-600 mb-4">Two-factor authentication is enabled.</p>
|
<p class="text-sm text-green-400 mb-4">Two-factor authentication is enabled.</p>
|
||||||
<button
|
<button
|
||||||
@click="disableTwoFactor"
|
@click="disableTwoFactor"
|
||||||
:disabled="disabling"
|
:disabled="disabling"
|
||||||
|
|||||||
@@ -9,37 +9,37 @@ defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const statusColors = {
|
const statusColors = {
|
||||||
active: 'bg-green-100 text-green-800',
|
active: 'bg-green-900/50 text-green-300 border border-green-800',
|
||||||
canceled: 'bg-red-100 text-red-800',
|
canceled: 'bg-red-900/50 text-red-300 border border-red-800',
|
||||||
past_due: 'bg-yellow-100 text-yellow-800',
|
past_due: 'bg-yellow-900/50 text-yellow-300 border border-yellow-800',
|
||||||
trialing: 'bg-blue-100 text-blue-800',
|
trialing: 'bg-blue-900/50 text-blue-300 border border-blue-800',
|
||||||
incomplete: 'bg-gray-100 text-gray-800',
|
incomplete: 'bg-gray-800 text-gray-400 border border-gray-700',
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="flex items-center justify-between mb-6">
|
||||||
<h1 class="text-2xl font-bold text-gray-900">Subscriptions</h1>
|
<h1 class="text-2xl font-bold text-white">Subscriptions</h1>
|
||||||
<Link href="/plans" class="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700">
|
<Link href="/plans" class="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700">
|
||||||
Browse Plans
|
Browse Plans
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="subscriptions.length === 0" class="bg-white rounded-lg border border-gray-200 shadow-sm p-12 text-center">
|
<div v-if="subscriptions.length === 0" class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-12 text-center">
|
||||||
<p class="text-gray-500 mb-4">You don't have any subscriptions yet.</p>
|
<p class="text-gray-500 mb-4">You don't have any subscriptions yet.</p>
|
||||||
<Link href="/plans" class="text-blue-600 hover:text-blue-500 text-sm font-medium">Browse Available Plans</Link>
|
<Link href="/plans" class="text-blue-400 hover:text-blue-300 text-sm font-medium">Browse Available Plans</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="space-y-4">
|
<div v-else class="space-y-4">
|
||||||
<div
|
<div
|
||||||
v-for="subscription in subscriptions"
|
v-for="subscription in subscriptions"
|
||||||
:key="subscription.id"
|
:key="subscription.id"
|
||||||
class="bg-white rounded-lg border border-gray-200 shadow-sm p-6"
|
class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-semibold text-gray-900">
|
<h3 class="text-lg font-semibold text-white">
|
||||||
{{ subscription.plan?.name || subscription.type }}
|
{{ subscription.plan?.name || subscription.type }}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-sm text-gray-500 mt-1">
|
<p class="text-sm text-gray-500 mt-1">
|
||||||
@@ -51,25 +51,25 @@ const statusColors = {
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span
|
<span
|
||||||
:class="statusColors[subscription.stripe_status] || 'bg-gray-100 text-gray-800'"
|
:class="statusColors[subscription.stripe_status] || 'bg-gray-800 text-gray-400'"
|
||||||
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium capitalize"
|
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium capitalize"
|
||||||
>
|
>
|
||||||
{{ subscription.stripe_status }}
|
{{ subscription.stripe_status }}
|
||||||
</span>
|
</span>
|
||||||
<Link
|
<Link
|
||||||
:href="`/subscriptions/${subscription.id}`"
|
:href="`/subscriptions/${subscription.id}`"
|
||||||
class="text-sm text-blue-600 hover:text-blue-500 font-medium"
|
class="text-sm text-blue-400 hover:text-blue-300 font-medium"
|
||||||
>
|
>
|
||||||
Manage
|
Manage
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="subscription.plan" class="mt-3 text-sm text-gray-600">
|
<div v-if="subscription.plan" class="mt-3 text-sm text-gray-400">
|
||||||
${{ parseFloat(subscription.plan.price).toFixed(2) }}/{{ subscription.plan.billing_cycle }}
|
${{ parseFloat(subscription.plan.price).toFixed(2) }}/{{ subscription.plan.billing_cycle }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="subscription.ends_at" class="mt-2 text-sm text-red-600">
|
<div v-if="subscription.ends_at" class="mt-2 text-sm text-red-400">
|
||||||
Cancels on {{ new Date(subscription.ends_at).toLocaleDateString() }}
|
Cancels on {{ new Date(subscription.ends_at).toLocaleDateString() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ const swapForm = useForm({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const statusColors = {
|
const statusColors = {
|
||||||
active: 'bg-green-100 text-green-800',
|
active: 'bg-green-900/50 text-green-300 border border-green-800',
|
||||||
canceled: 'bg-red-100 text-red-800',
|
canceled: 'bg-red-900/50 text-red-300 border border-red-800',
|
||||||
past_due: 'bg-yellow-100 text-yellow-800',
|
past_due: 'bg-yellow-900/50 text-yellow-300 border border-yellow-800',
|
||||||
trialing: 'bg-blue-100 text-blue-800',
|
trialing: 'bg-blue-900/50 text-blue-300 border border-blue-800',
|
||||||
incomplete: 'bg-gray-100 text-gray-800',
|
incomplete: 'bg-gray-800 text-gray-400 border border-gray-700',
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelSubscription = () => {
|
const cancelSubscription = () => {
|
||||||
@@ -45,21 +45,21 @@ const swapPlan = () => {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<Link href="/subscriptions" class="text-sm text-blue-600 hover:text-blue-500">← Back to Subscriptions</Link>
|
<Link href="/subscriptions" class="text-sm text-blue-400 hover:text-blue-300">← Back to Subscriptions</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Subscription Details</h1>
|
<h1 class="text-2xl font-bold text-white mb-6">Subscription Details</h1>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
<!-- Subscription Info -->
|
<!-- Subscription Info -->
|
||||||
<div class="lg:col-span-2 space-y-6">
|
<div class="lg:col-span-2 space-y-6">
|
||||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h2 class="text-lg font-semibold text-gray-900">
|
<h2 class="text-lg font-semibold text-white">
|
||||||
{{ subscription.plan?.name || subscription.type }}
|
{{ subscription.plan?.name || subscription.type }}
|
||||||
</h2>
|
</h2>
|
||||||
<span
|
<span
|
||||||
:class="statusColors[subscription.stripe_status] || 'bg-gray-100 text-gray-800'"
|
:class="statusColors[subscription.stripe_status] || 'bg-gray-800 text-gray-400'"
|
||||||
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium capitalize"
|
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium capitalize"
|
||||||
>
|
>
|
||||||
{{ subscription.stripe_status }}
|
{{ subscription.stripe_status }}
|
||||||
@@ -69,46 +69,46 @@ const swapPlan = () => {
|
|||||||
<dl class="grid grid-cols-2 gap-4 text-sm">
|
<dl class="grid grid-cols-2 gap-4 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<dt class="text-gray-500">Gateway</dt>
|
<dt class="text-gray-500">Gateway</dt>
|
||||||
<dd class="mt-1 text-gray-900 capitalize">{{ subscription.gateway || 'stripe' }}</dd>
|
<dd class="mt-1 text-gray-200 capitalize">{{ subscription.gateway || 'stripe' }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subscription.plan">
|
<div v-if="subscription.plan">
|
||||||
<dt class="text-gray-500">Price</dt>
|
<dt class="text-gray-500">Price</dt>
|
||||||
<dd class="mt-1 text-gray-900">${{ parseFloat(subscription.plan.price).toFixed(2) }}/{{ subscription.plan.billing_cycle }}</dd>
|
<dd class="mt-1 text-gray-200">${{ parseFloat(subscription.plan.price).toFixed(2) }}/{{ subscription.plan.billing_cycle }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subscription.current_period_start">
|
<div v-if="subscription.current_period_start">
|
||||||
<dt class="text-gray-500">Current Period Start</dt>
|
<dt class="text-gray-500">Current Period Start</dt>
|
||||||
<dd class="mt-1 text-gray-900">{{ new Date(subscription.current_period_start).toLocaleDateString() }}</dd>
|
<dd class="mt-1 text-gray-200">{{ new Date(subscription.current_period_start).toLocaleDateString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subscription.current_period_end">
|
<div v-if="subscription.current_period_end">
|
||||||
<dt class="text-gray-500">Current Period End</dt>
|
<dt class="text-gray-500">Current Period End</dt>
|
||||||
<dd class="mt-1 text-gray-900">{{ new Date(subscription.current_period_end).toLocaleDateString() }}</dd>
|
<dd class="mt-1 text-gray-200">{{ new Date(subscription.current_period_end).toLocaleDateString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subscription.ends_at">
|
<div v-if="subscription.ends_at">
|
||||||
<dt class="text-gray-500">Cancels On</dt>
|
<dt class="text-gray-500">Cancels On</dt>
|
||||||
<dd class="mt-1 text-red-600">{{ new Date(subscription.ends_at).toLocaleDateString() }}</dd>
|
<dd class="mt-1 text-red-400">{{ new Date(subscription.ends_at).toLocaleDateString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt class="text-gray-500">Created</dt>
|
<dt class="text-gray-500">Created</dt>
|
||||||
<dd class="mt-1 text-gray-900">{{ new Date(subscription.created_at).toLocaleDateString() }}</dd>
|
<dd class="mt-1 text-gray-200">{{ new Date(subscription.created_at).toLocaleDateString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Change Plan -->
|
<!-- Change Plan -->
|
||||||
<div v-if="availablePlans.length > 0 && subscription.stripe_status === 'active'" class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div v-if="availablePlans.length > 0 && subscription.stripe_status === 'active'" class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">Change Plan</h2>
|
<h2 class="text-lg font-semibold text-white mb-4">Change Plan</h2>
|
||||||
|
|
||||||
<form @submit.prevent="swapPlan" class="space-y-4">
|
<form @submit.prevent="swapPlan" class="space-y-4">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label v-for="plan in availablePlans" :key="plan.id"
|
<label v-for="plan in availablePlans" :key="plan.id"
|
||||||
class="flex items-center justify-between p-3 border rounded-md cursor-pointer"
|
class="flex items-center justify-between p-3 border rounded-md cursor-pointer"
|
||||||
:class="swapForm.plan_id == plan.id ? 'border-blue-500 bg-blue-50' : 'border-gray-200'"
|
:class="swapForm.plan_id == plan.id ? 'border-blue-500 bg-blue-900/20' : 'border-gray-700'"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input v-model="swapForm.plan_id" type="radio" :value="plan.id" class="mr-3">
|
<input v-model="swapForm.plan_id" type="radio" :value="plan.id" class="mr-3">
|
||||||
<span class="text-sm font-medium">{{ plan.name }}</span>
|
<span class="text-sm font-medium text-gray-200">{{ plan.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm text-gray-600">${{ parseFloat(plan.price).toFixed(2) }}/{{ plan.billing_cycle }}</span>
|
<span class="text-sm text-gray-400">${{ parseFloat(plan.price).toFixed(2) }}/{{ plan.billing_cycle }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -126,12 +126,12 @@ const swapPlan = () => {
|
|||||||
<!-- Actions Sidebar -->
|
<!-- Actions Sidebar -->
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<!-- Cancel -->
|
<!-- Cancel -->
|
||||||
<div v-if="subscription.stripe_status === 'active' && !subscription.ends_at" class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div v-if="subscription.stripe_status === 'active' && !subscription.ends_at" class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">Cancel Subscription</h2>
|
<h2 class="text-lg font-semibold text-white mb-4">Cancel Subscription</h2>
|
||||||
|
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<label class="flex items-center text-sm">
|
<label class="flex items-center text-sm text-gray-300">
|
||||||
<input v-model="cancelImmediately" type="checkbox" class="mr-2">
|
<input v-model="cancelImmediately" type="checkbox" class="mr-2 rounded bg-gray-800 border-gray-700">
|
||||||
Cancel immediately (no grace period)
|
Cancel immediately (no grace period)
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -146,8 +146,8 @@ const swapPlan = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Resume -->
|
<!-- Resume -->
|
||||||
<div v-if="subscription.ends_at && subscription.stripe_status !== 'canceled'" class="bg-white rounded-lg border border-gray-200 shadow-sm p-6">
|
<div v-if="subscription.ends_at && subscription.stripe_status !== 'canceled'" class="bg-gray-900 rounded-lg border border-gray-800 shadow-sm p-6">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">Resume Subscription</h2>
|
<h2 class="text-lg font-semibold text-white mb-4">Resume Subscription</h2>
|
||||||
<p class="text-sm text-gray-500 mb-3">Your subscription is set to cancel. You can resume it before it expires.</p>
|
<p class="text-sm text-gray-500 mb-3">Your subscription is set to cancel. You can resume it before it expires.</p>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
@vite(['resources/js/app.js'])
|
@vite(['resources/js/app.js'])
|
||||||
@inertiaHead
|
@inertiaHead
|
||||||
</head>
|
</head>
|
||||||
<body class="font-sans antialiased">
|
<body class="font-sans antialiased bg-gray-950 text-gray-100">
|
||||||
@inertia
|
@inertia
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user