windows/app/pages/register.vue

556 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="auth-page">
<!-- Windows-style Auth Window -->
<div class="auth-window">
<!-- Title Bar -->
<div class="title-bar">
<div class="title-bar-content">
<div class="title-bar-icon">📝</div>
<div class="title-bar-text">{{ $t('auth.register.title') }}</div>
</div>
<div class="title-bar-controls">
<button class="control-btn close-btn" @click="goHome">
<span class="control-btn-icon">×</span>
</button>
</div>
</div>
<!-- Window Content -->
<div class="window-content">
<div class="auth-content">
<!-- Header -->
<div class="auth-header">
<h1 class="auth-title">{{ $t('auth.register.title') }}</h1>
<p class="auth-subtitle">{{ $t('auth.register.subtitle') }}</p>
</div>
<!-- Form -->
<form @submit.prevent="handleRegister" class="auth-form">
<!-- Full Name Field -->
<div class="form-group">
<label for="fullName" class="form-label">{{ $t('auth.register.fullName') }}</label>
<input
id="fullName"
v-model="form.fullName"
type="text"
:placeholder="$t('auth.register.fullNamePlaceholder')"
class="form-input"
:class="{ 'error': errors.fullName }"
required
/>
<span v-if="errors.fullName" class="error-message">{{ errors.fullName }}</span>
</div>
<!-- Email Field -->
<div class="form-group">
<label for="email" class="form-label">{{ $t('auth.register.email') }}</label>
<input
id="email"
v-model="form.email"
type="email"
:placeholder="$t('auth.register.emailPlaceholder')"
class="form-input"
:class="{ 'error': errors.email }"
required
/>
<span v-if="errors.email" class="error-message">{{ errors.email }}</span>
</div>
<!-- Password Field -->
<div class="form-group">
<label for="password" class="form-label">{{ $t('auth.register.password') }}</label>
<input
id="password"
v-model="form.password"
type="password"
:placeholder="$t('auth.register.passwordPlaceholder')"
class="form-input"
:class="{ 'error': errors.password }"
required
/>
<span v-if="errors.password" class="error-message">{{ errors.password }}</span>
</div>
<!-- Confirm Password Field -->
<div class="form-group">
<label for="confirmPassword" class="form-label">{{ $t('auth.register.confirmPassword') }}</label>
<input
id="confirmPassword"
v-model="form.confirmPassword"
type="password"
:placeholder="$t('auth.register.confirmPasswordPlaceholder')"
class="form-input"
:class="{ 'error': errors.confirmPassword }"
required
/>
<span v-if="errors.confirmPassword" class="error-message">{{ errors.confirmPassword }}</span>
</div>
<!-- Terms and Conditions -->
<div class="form-group">
<label class="checkbox-label">
<input v-model="form.agreeTerms" type="checkbox" class="checkbox-input" required />
<span class="checkbox-text">
{{ $t('auth.register.agreeTerms') }}
<button type="button" class="terms-link" @click="showTerms">
{{ $t('auth.register.termsAndConditions') }}
</button>
</span>
</label>
</div>
<!-- Submit Button -->
<button type="submit" class="auth-button" :disabled="isLoading">
<span v-if="isLoading" class="loading-spinner"></span>
{{ isLoading ? $t('auth.common.loading') : $t('auth.register.registerButton') }}
</button>
</form>
<!-- Sign In Link -->
<div class="auth-footer">
<p class="auth-footer-text">
{{ $t('auth.register.hasAccount') }}
<button class="auth-link" @click="goToLogin">
{{ $t('auth.register.signIn') }}
</button>
</p>
</div>
</div>
</div>
</div>
<!-- Background Pattern -->
<div class="auth-background">
<div class="pattern-grid"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
const router = useRouter();
const { t } = useI18n();
// Form data
const form = reactive({
fullName: '',
email: '',
password: '',
confirmPassword: '',
agreeTerms: false
});
// Form validation
const errors = reactive({
fullName: '',
email: '',
password: '',
confirmPassword: ''
});
// Loading state
const isLoading = ref(false);
// Validation functions
const validateEmail = (email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const validateForm = () => {
errors.fullName = '';
errors.email = '';
errors.password = '';
errors.confirmPassword = '';
if (!form.fullName) {
errors.fullName = t('auth.common.required');
return false;
}
if (!form.email) {
errors.email = t('auth.common.required');
return false;
}
if (!validateEmail(form.email)) {
errors.email = t('auth.common.invalidEmail');
return false;
}
if (!form.password) {
errors.password = t('auth.common.required');
return false;
}
if (form.password.length < 8) {
errors.password = t('auth.common.passwordTooShort');
return false;
}
if (!form.confirmPassword) {
errors.confirmPassword = t('auth.common.required');
return false;
}
if (form.password !== form.confirmPassword) {
errors.confirmPassword = t('auth.common.passwordsDoNotMatch');
return false;
}
if (!form.agreeTerms) {
alert(t('auth.common.required'));
return false;
}
return true;
};
// Event handlers
const handleRegister = async () => {
if (!validateForm()) return;
isLoading.value = true;
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
// Success - redirect to home
router.push('/');
} catch (error) {
console.error('Registration error:', error);
} finally {
isLoading.value = false;
}
};
const showTerms = () => {
// TODO: Implement terms and conditions modal
alert('Terms and Conditions coming soon!');
};
const goToLogin = () => {
router.push('/login');
};
const goHome = () => {
router.push('/');
};
</script>
<style scoped>
.auth-page {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: var(--background-desktop);
display: flex;
align-items: center;
justify-content: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
overflow: hidden;
}
.auth-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.1;
z-index: 0;
}
.pattern-grid {
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.1) 1px, transparent 1px);
background-size: 20px 20px;
animation: grid-move 20s linear infinite;
}
@keyframes grid-move {
0% { transform: translate(0, 0); }
100% { transform: translate(20px, 20px); }
}
.auth-window {
position: relative;
z-index: 1;
width: 90%;
max-width: 400px;
background: var(--window-background);
border: 1px solid var(--window-border-color);
border-radius: var(--rounded-window);
box-shadow: var(--shadow-window);
overflow: hidden;
animation: window-appear 0.5s ease-out;
}
@keyframes window-appear {
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.title-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: var(--title-bar-background);
color: var(--title-bar-text-color);
padding: 8px 12px;
border-bottom: 1px solid var(--window-border-color);
}
.title-bar-content {
display: flex;
align-items: center;
gap: 8px;
}
.title-bar-icon {
font-size: 16px;
}
.title-bar-text {
font-weight: 600;
font-size: 14px;
}
.title-bar-controls {
display: flex;
gap: 4px;
}
.control-btn {
width: 20px;
height: 20px;
border: none;
border-radius: var(--rounded-control-btn);
background: var(--control-btn-close-bg);
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
font-size: 12px;
font-weight: bold;
}
.control-btn:hover {
transform: scale(1.1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.window-content {
padding: 32px;
color: var(--content-text-color);
}
.auth-content {
text-align: center;
}
.auth-header {
margin-bottom: 32px;
}
.auth-title {
font-size: 28px;
font-weight: 700;
margin-bottom: 8px;
color: var(--content-text-color);
}
.auth-subtitle {
font-size: 16px;
color: var(--content-text-color);
opacity: 0.7;
}
.auth-form {
text-align: left;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
font-size: 14px;
font-weight: 600;
margin-bottom: 8px;
color: var(--content-text-color);
}
.form-input {
width: 100%;
padding: 12px 16px;
border: 1px solid var(--window-border-color);
border-radius: var(--rounded-button);
background: var(--taskbar-item-background);
color: var(--content-text-color);
font-size: 14px;
transition: all 0.3s ease;
box-sizing: border-box;
}
.form-input:focus {
outline: none;
border-color: var(--control-btn-maximize-bg);
box-shadow: 0 0 0 2px rgba(76, 209, 55, 0.2);
}
.form-input.error {
border-color: var(--control-btn-close-bg);
}
.error-message {
display: block;
font-size: 12px;
color: var(--control-btn-close-bg);
margin-top: 4px;
}
.checkbox-label {
display: flex;
align-items: flex-start;
cursor: pointer;
font-size: 14px;
color: var(--content-text-color);
line-height: 1.4;
}
.checkbox-input {
margin-right: 8px;
margin-top: 2px;
width: 16px;
height: 16px;
flex-shrink: 0;
}
.checkbox-text {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.terms-link {
background: none;
border: none;
color: var(--control-btn-maximize-bg);
font-size: 14px;
cursor: pointer;
text-decoration: underline;
margin-left: 4px;
}
.terms-link:hover {
opacity: 0.8;
}
.auth-button {
width: 100%;
padding: 14px;
background: var(--control-btn-maximize-bg);
color: white;
border: none;
border-radius: var(--rounded-button);
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.auth-button:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(76, 209, 55, 0.4);
}
.auth-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.loading-spinner {
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.auth-footer {
margin-top: 24px;
text-align: center;
}
.auth-footer-text {
font-size: 14px;
color: var(--content-text-color);
opacity: 0.7;
}
.auth-link {
background: none;
border: none;
color: var(--control-btn-maximize-bg);
font-size: 14px;
cursor: pointer;
text-decoration: underline;
margin-left: 4px;
}
.auth-link:hover {
opacity: 0.8;
}
/* Responsive Design */
@media (max-width: 768px) {
.auth-window {
width: 95%;
margin: 20px;
}
.window-content {
padding: 24px;
}
.auth-title {
font-size: 24px;
}
}
/* Dark/Light theme transitions */
.auth-page {
transition: background-color 0.3s ease;
}
.auth-window {
transition: background-color 0.3s ease, border-color 0.3s ease;
}
.window-content {
transition: color 0.3s ease;
}
</style>