Files
yourpart3/frontend/src/views/home/NoLoginView.vue

448 lines
13 KiB
Vue

<template>
<div class="no-login-view">
<div class="beta-banner" role="status" aria-live="polite">
<strong>{{ $t('home.betaNoticeLabel') }}</strong> {{ $t('home.betaNoticeText') }}
</div>
<div class="home-structure">
<div class="mascot">
<Character3D gender="male" />
</div>
<div class="actions">
<section class="actions-panel actions-panel--story surface-card">
<div class="panel-intro">
<span class="panel-kicker">Dein Einstieg</span>
<h2>{{ $t('home.nologin.welcome') }}</h2>
<p>{{ $t('home.nologin.description') }}</p>
</div>
<div class="story-highlight">
<p>
YourPart verbindet Community, Echtzeit-Chat, Foren, Bildergalerie und das Aufbauspiel
<em>Falukant</em> in einer Plattform. Der Fokus liegt auf Austausch, spielerischer Tiefe und
einer wachsenden Produktwelt.
</p>
</div>
<div class="story-block">
<h3>{{ $t('home.nologin.expected.title') }}</h3>
<ul class="feature-list">
<li v-html="$t('home.nologin.expected.items.chat')"></li>
<li v-html="$t('home.nologin.expected.items.social')"></li>
<li v-html="$t('home.nologin.expected.items.forum')"></li>
<li v-html="$t('home.nologin.expected.items.falukant')"></li>
<li v-html="$t('home.nologin.expected.items.minigames')"></li>
<li v-html="$t('home.nologin.expected.items.multilingual')"></li>
</ul>
</div>
<div class="story-columns">
<article>
<h3>{{ $t('home.nologin.falukantShort.title') }}</h3>
<p>{{ $t('home.nologin.falukantShort.text') }}</p>
</article>
<article>
<h3>{{ $t('home.nologin.privacyBeta.title') }}</h3>
<p>{{ $t('home.nologin.privacyBeta.text') }}</p>
</article>
</div>
<div class="story-cta">
<h3>{{ $t('home.nologin.getStarted.title') }}</h3>
<p>{{ $t('home.nologin.getStarted.text', { register: $t('home.nologin.login.register') }) }}</p>
</div>
</section>
<section class="actions-panel actions-panel--access surface-card">
<div class="login-panel">
<span class="panel-kicker">Direkt starten</span>
<h2>{{ $t('home.nologin.login.submit') }}</h2>
<p class="login-panel__hint">Mit bestehendem Konto direkt einloggen oder alternativ ohne Konto den Random-Chat testen.</p>
<div class="quick-access-actions">
<button type="button" class="primary-action" @click="doLogin">{{ $t('home.nologin.login.submit') }}</button>
<button type="button" class="secondary-action" @click="openRegisterDialog">{{ $t('home.nologin.login.register') }}</button>
</div>
<div class="login-fields">
<input ref="usernameInput" v-model="username" size="20" type="text" :placeholder="$t('home.nologin.login.name')"
:title="$t('home.nologin.login.namedescription')" @keydown.enter="focusPassword">
<input v-model="password" size="20" type="password"
:placeholder="$t('home.nologin.login.password')"
:title="$t('home.nologin.login.passworddescription')" @keydown.enter="doLogin"
ref="passwordInput">
</div>
<div class="stay-logged-in-row">
<label class="stay-logged-in-label">
<input class="stay-logged-in-checkbox" type="checkbox">
<span>{{ $t('home.nologin.login.stayLoggedIn') }}</span>
</label>
</div>
</div>
<div class="access-split">
<article class="access-card">
<h3>{{ $t('home.nologin.randomchat') }}</h3>
<p>Ohne lange Vorbereitung direkt in spontane Begegnungen und offene Gespraeche starten.</p>
<button type="button" class="secondary-action" @click="openRandomChat">{{ $t('home.nologin.startrandomchat') }}</button>
</article>
<article class="access-card">
<h3>Konto und Zugang</h3>
<p>Neu hier oder Passwort vergessen? Von hier aus gelangst du direkt in Registrierung und Wiederherstellung.</p>
<div class="access-links">
<span @click="openPasswordResetDialog" class="link">{{
$t('home.nologin.login.lostpassword') }}</span>
<span @click="openRegisterDialog" class="link">{{ $t('home.nologin.login.register') }}</span>
</div>
</article>
</div>
</section>
</div>
<div class="mascot">
<Character3D gender="female" />
</div>
<RandomChatDialog ref="randomChatDialog" />
<RegisterDialog ref="registerDialog" />
<PasswordResetDialog ref="passwordResetDialog" />
</div>
</div>
</template>
<script>
import RandomChatDialog from '@/dialogues/chat/RandomChatDialog.vue';
import RegisterDialog from '@/dialogues/auth/RegisterDialog.vue';
import PasswordResetDialog from '@/dialogues/auth/PasswordResetDialog.vue';
import Character3D from '@/components/Character3D.vue';
import apiClient from '@/utils/axios.js';
import { mapActions } from 'vuex';
export default {
name: 'HomeNoLoginView',
data() {
return {
username: '',
password: '',
};
},
components: {
RandomChatDialog,
RegisterDialog,
PasswordResetDialog,
Character3D,
},
methods: {
...mapActions(['login']),
openRandomChat() {
const dlg = this.$refs.randomChatDialog;
if (dlg && typeof dlg.open === 'function') dlg.open();
},
openRegisterDialog() {
const dlg = this.$refs.registerDialog;
if (dlg && typeof dlg.open === 'function') dlg.open();
},
openPasswordResetDialog() {
const dlg = this.$refs.passwordResetDialog;
if (dlg && typeof dlg.open === 'function') dlg.open();
},
focusPassword() {
this.$refs.passwordInput.focus();
},
async doLogin() {
try {
const response = await apiClient.post('/api/auth/login', { username: this.username, password: this.password });
this.login(response.data);
} catch (error) {
const errorKey = error?.response?.data?.error || 'network';
this.$root.$refs.errorDialog.open(`tr:error.${errorKey}`);
}
}
},
mounted() {
this.$nextTick(() => {
this.$refs.usernameInput?.focus?.();
});
}
};
</script>
<style scoped>
.beta-banner {
width: min(100%, var(--content-max-width));
background: linear-gradient(180deg, #fff2cf 0%, #fde7b2 100%);
border: 1px solid rgba(201, 130, 31, 0.24);
color: #8a5a12;
padding: 10px 14px;
margin: 0 0 14px 0;
text-align: center;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-soft);
}
.home-structure {
display: flex;
align-items: stretch;
justify-content: center;
gap: 1.4rem;
width: 100%;
height: 100%;
flex: 1;
min-height: 0;
}
.home-structure>div {
text-align: center;
display: flex;
min-height: 0;
}
.mascot {
flex: 0 0 clamp(180px, 22%, 280px);
justify-content: center;
align-items: stretch;
background: linear-gradient(180deg, #fff5e8 0%, #fce7ca 100%);
border: 1px solid rgba(248, 162, 43, 0.16);
border-radius: var(--radius-lg);
box-shadow: 0 10px 24px rgba(93, 64, 55, 0.08);
overflow: hidden;
align-self: center;
height: clamp(320px, 68vh, 560px);
min-height: 320px;
max-height: 560px;
}
.actions {
display: flex;
flex-direction: column;
gap: 1rem;
flex: 1 1 auto;
min-height: 0;
}
.actions-panel {
flex: 1;
min-height: 0;
background:
linear-gradient(180deg, rgba(255, 251, 246, 0.96) 0%, rgba(248, 240, 231, 0.96) 100%);
color: #5D4037;
display: flex;
flex-direction: column;
overflow: auto;
padding: 1.2rem 1.25rem;
text-align: left;
}
.actions-panel h2,
.actions-panel h3 {
width: 100%;
}
.panel-kicker {
display: inline-block;
margin-bottom: 0.7rem;
padding: 0.3rem 0.65rem;
border-radius: 999px;
background: rgba(248, 162, 43, 0.12);
color: #8a5411;
font-size: 0.74rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.panel-intro,
.story-highlight,
.story-block,
.story-columns,
.story-cta,
.login-panel,
.access-split {
width: 100%;
}
.story-highlight {
padding: 1rem 1.1rem;
margin: 0.8rem 0 1rem;
border-radius: var(--radius-lg);
background: rgba(248, 162, 43, 0.08);
border: 1px solid rgba(248, 162, 43, 0.12);
}
.story-block {
margin-bottom: 1rem;
}
.feature-list {
padding-left: 1.1rem;
}
.feature-list li + li {
margin-top: 0.55rem;
}
.story-columns {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.9rem;
margin-bottom: 1rem;
}
.story-columns article,
.story-cta,
.access-card {
padding: 1rem 1.05rem;
border-radius: var(--radius-lg);
background: rgba(255, 255, 255, 0.68);
border: 1px solid var(--color-border);
}
.login-panel {
padding: 1rem 1.05rem;
border-radius: var(--radius-lg);
background: rgba(255, 255, 255, 0.7);
border: 1px solid var(--color-border);
margin-bottom: 1rem;
}
.login-panel__hint {
margin: 0 0 0.9rem;
color: var(--color-text-secondary);
}
.quick-access-actions {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-bottom: 0.9rem;
}
.login-fields {
display: grid;
gap: 0.8rem;
}
.primary-action,
.secondary-action {
align-self: flex-start;
}
.access-split {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.9rem;
}
.access-card p {
margin-bottom: 0.9rem;
}
.access-links {
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
}
.stay-logged-in-row {
width: 100%;
display: flex;
justify-content: flex-start;
margin-top: 0.35rem;
}
.stay-logged-in-label {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
font-size: 0.95rem;
}
.stay-logged-in-checkbox {
width: 16px;
min-width: 16px;
height: 16px;
min-height: 16px;
margin: 0;
padding: 0;
flex: 0 0 16px;
accent-color: var(--color-primary-orange);
box-shadow: none;
}
.seo-content {
max-width: 1000px;
margin: 24px auto 0 auto;
padding: 0 16px 40px 16px;
color: #5D4037;
background-color: #FFF4F0;
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 4px;
}
.seo-content h1 {
font-size: 28px;
margin: 0 0 8px 0;
}
.seo-content h2 {
font-size: 20px;
margin: 18px 0 6px 0;
color: #444;
}
.seo-content p {
line-height: 1.6;
margin: 0 0 8px 0;
}
.seo-content ul {
margin: 0 0 8px 20px;
}
/* Scrollbarer Bereich für "Was dich erwartet" */
.seo-content .expected {
max-height: 200px;
overflow: auto;
padding-right: 8px;
background-color: #fdf1db;
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px;
margin: 12px 0;
}
.no-login-view {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
width: 100%;
height: 100%;
min-height: 0;
overflow: hidden;
}
@media (max-width: 960px) {
.home-structure {
flex-direction: column;
gap: 1rem;
overflow: auto;
}
.mascot {
min-height: 260px;
height: 260px;
flex: 0 0 260px;
}
.actions {
min-height: auto;
}
.actions-panel {
min-height: 260px;
}
.story-columns,
.access-split {
grid-template-columns: 1fr;
}
}
</style>