feat(settings): add Billing, Danger Zone, Domains DNS, Notification, Player, and Security settings pages

- Implemented Billing page with wallet balance, current plan, usage stats, available plans, and payment history.
- Created Danger Zone page for account deletion and data clearing actions with confirmation prompts.
- Developed Domains DNS page for managing whitelisted domains for iframe embedding, including add and remove functionality.
- Added Notification Settings page to configure email, push, marketing, and Telegram notifications.
- Introduced Player Settings page to customize video player behavior such as autoplay, loop, and controls visibility.
- Established Security and Connected Accounts page for managing user profile, two-factor authentication, and connected accounts.
This commit is contained in:
2026-03-01 22:49:30 +07:00
parent c6924afe5b
commit cd9aab8979
65 changed files with 3150 additions and 1133 deletions

View File

@@ -0,0 +1,196 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useToast } from 'primevue/usetoast';
import InputText from 'primevue/inputtext';
import IconField from 'primevue/iconfield';
import InputIcon from 'primevue/inputicon';
import Dialog from 'primevue/dialog';
import Button from 'primevue/button';
import LockIcon from '@/components/icons/LockIcon.vue';
import TelegramIcon from '@/components/icons/TelegramIcon.vue';
const toast = useToast();
const props = defineProps<{
dialogVisible: boolean;
error: string;
loading: boolean;
currentPassword: string;
newPassword: string;
confirmPassword: string;
emailConnected: boolean;
telegramConnected: boolean;
telegramUsername: string;
}>();
const emit = defineEmits<{
(e: 'update:dialogVisible', value: boolean): void;
(e: 'update:currentPassword', value: string): void;
(e: 'update:newPassword', value: string): void;
(e: 'update:confirmPassword', value: string): void;
(e: 'close'): void;
(e: 'change-password'): void;
(e: 'connect-telegram'): void;
(e: 'disconnect-telegram'): void;
}>();
const handleChangePassword = () => {
emit('change-password');
};
</script>
<template>
<div class="bg-surface border border-border rounded-lg">
<!-- Header -->
<div class="px-6 py-4 border-b border-border">
<h3 class="text-sm font-semibold text-foreground mb-3">Connected Accounts</h3>
</div>
<!-- Content -->
<div class="p-6 space-y-4">
<!-- Email Connection -->
<div class="flex items-center justify-between p-4 rounded-md bg-muted/30">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-full bg-info/10 flex items-center justify-center shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect width="20" height="16" x="2" y="4" rx="2"/>
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>
</svg>
</div>
<div>
<p class="text-sm font-medium text-foreground">Email</p>
<p class="text-xs text-foreground/60 mt-0.5">
{{ emailConnected ? 'Connected' : 'Not connected' }}
</p>
</div>
</div>
<span class="text-xs font-medium px-2 py-1 rounded" :class="emailConnected ? 'text-success bg-success/10' : 'text-muted bg-muted/20'">
{{ emailConnected ? 'Connected' : 'Disconnected' }}
</span>
</div>
<!-- Telegram Connection -->
<div class="flex items-center justify-between p-4 rounded-md bg-muted/30">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-full bg-[#0088cc]/10 flex items-center justify-center shrink-0">
<TelegramIcon class="w-5 h-5 text-[#0088cc]" />
</div>
<div>
<p class="text-sm font-medium text-foreground">Telegram</p>
<p class="text-xs text-foreground/60 mt-0.5">
{{ telegramConnected ? (telegramUsername || 'Connected') : 'Get notified via Telegram' }}
</p>
</div>
</div>
<Button
v-if="telegramConnected"
label="Disconnect"
size="small"
text
severity="danger"
@click="$emit('disconnect-telegram')"
class="press-animated"
/>
<Button
v-else
label="Connect"
size="small"
@click="$emit('connect-telegram')"
class="press-animated"
/>
</div>
</div>
<!-- Change Password Dialog -->
<Dialog
:visible="dialogVisible"
@update:visible="$emit('update:dialogVisible', $event)"
modal
header="Change Password"
:style="{ width: '26rem' }"
>
<div class="space-y-4">
<p class="text-sm text-foreground/70">
Enter your current password and choose a new password.
</p>
<!-- Error Message -->
<div v-if="error" class="bg-danger/10 border border-danger text-danger text-sm rounded-md p-3">
{{ error }}
</div>
<!-- Current Password -->
<div class="grid gap-2">
<label for="currentPassword" class="text-sm font-medium text-foreground">Current Password</label>
<IconField>
<InputIcon>
<LockIcon class="w-5 h-5" />
</InputIcon>
<InputText
id="currentPassword"
:model-value="currentPassword"
type="password"
placeholder="Enter current password"
class="w-full"
@update:model-value="$emit('update:currentPassword', $event)"
/>
</IconField>
</div>
<!-- New Password -->
<div class="grid gap-2">
<label for="newPassword" class="text-sm font-medium text-foreground">New Password</label>
<IconField>
<InputIcon>
<LockIcon class="w-5 h-5" />
</InputIcon>
<InputText
id="newPassword"
:model-value="newPassword"
type="password"
placeholder="Enter new password"
class="w-full"
@update:model-value="$emit('update:newPassword', $event)"
/>
</IconField>
</div>
<!-- Confirm Password -->
<div class="grid gap-2">
<label for="confirmPassword" class="text-sm font-medium text-foreground">Confirm New Password</label>
<IconField>
<InputIcon>
<LockIcon class="w-5 h-5" />
</InputIcon>
<InputText
id="confirmPassword"
:model-value="confirmPassword"
type="password"
placeholder="Confirm new password"
class="w-full"
@update:model-value="$emit('update:confirmPassword', $event)"
/>
</IconField>
</div>
</div>
<template #footer>
<div class="flex justify-end gap-3">
<Button
label="Cancel"
text
severity="secondary"
@click="$emit('close')"
:disabled="loading"
class="press-animated"
/>
<Button
label="Change Password"
@click="handleChangePassword"
:loading="loading"
class="press-animated"
/>
</div>
</template>
</Dialog>
</div>
</template>