292 lines
6.6 KiB
Svelte
292 lines
6.6 KiB
Svelte
<script>
|
||
import { progressStorage } from '$lib/utils/storage';
|
||
|
||
export let data; // { error } из load()
|
||
|
||
let activeTab = 'local';
|
||
let email = '';
|
||
let password = '';
|
||
let moodleUsername = '';
|
||
let moodlePassword = '';
|
||
let error = data?.error ?? '';
|
||
let loading = false;
|
||
let showPass = false;
|
||
let showMPass = false;
|
||
|
||
async function loginLocal() {
|
||
loading = true;
|
||
error = '';
|
||
try {
|
||
const res = await fetch('/auth/callback/local', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: new URLSearchParams({ email, password }),
|
||
});
|
||
|
||
if (res.url.includes('/login')) {
|
||
error = 'Неверный логин или пароль';
|
||
} else {
|
||
await progressStorage.transferToDB();
|
||
window.location.href = '/';
|
||
}
|
||
} catch {
|
||
error = 'Ошибка сети';
|
||
}
|
||
loading = false;
|
||
}
|
||
|
||
async function loginMoodle() {
|
||
loading = true;
|
||
error = '';
|
||
try {
|
||
const res = await fetch('/auth/callback/moodle', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: new URLSearchParams({ username: moodleUsername, password: moodlePassword }),
|
||
});
|
||
if (res.url.includes('/login')) {
|
||
error = 'Неверный логин или пароль Moodle';
|
||
} else {
|
||
await progressStorage.transferToDB();
|
||
window.location.href = '/';
|
||
}
|
||
} catch {
|
||
error = 'Ошибка сети';
|
||
}
|
||
loading = false;
|
||
}
|
||
</script>
|
||
|
||
<div class="page">
|
||
<div class="card">
|
||
<a href="/" class="close-btn" title="На главную">✕</a>
|
||
<h1>Вход</h1>
|
||
|
||
<div class="tabs">
|
||
<button class="tab" class:active={activeTab === 'local'}
|
||
on:click={() => { activeTab = 'local'; error = ''; }}>
|
||
Свой аккаунт
|
||
</button>
|
||
<button class="tab" class:active={activeTab === 'moodle'}
|
||
on:click={() => { activeTab = 'moodle'; error = ''; }}>
|
||
Войти через Moodle
|
||
</button>
|
||
</div>
|
||
|
||
{#if activeTab === 'local'}
|
||
<div class="form">
|
||
<input type="email" bind:value={email} placeholder="Email" />
|
||
<div class="pass-wrap">
|
||
<input type={showPass ? 'text' : 'password'}
|
||
bind:value={password} placeholder="Пароль"
|
||
autocomplete="current-password" />
|
||
<button type="button" class="eye" on:click={() => showPass = !showPass}>
|
||
{showPass ? '🙈' : '👁️'}
|
||
</button>
|
||
</div>
|
||
<button class="btn-primary" on:click={loginLocal} disabled={loading}>
|
||
{loading ? 'Вход...' : 'Войти'}
|
||
</button>
|
||
</div>
|
||
<p class="link">Нет аккаунта? <a href="/register">Зарегистрироваться</a></p>
|
||
|
||
{:else}
|
||
<div class="form">
|
||
<p class="hint">Введите логин и пароль от аккаунта Moodle</p>
|
||
<input type="text" bind:value={moodleUsername} placeholder="Логин Moodle" />
|
||
<div class="pass-wrap">
|
||
<input type={showMPass ? 'text' : 'password'}
|
||
bind:value={moodlePassword} placeholder="Пароль Moodle"
|
||
autocomplete="current-password" />
|
||
<button type="button" class="eye" on:click={() => showMPass = !showMPass}>
|
||
{showMPass ? '🙈' : '👁️'}
|
||
</button>
|
||
</div>
|
||
<button class="btn-moodle" on:click={loginMoodle} disabled={loading}>
|
||
{loading ? 'Проверка...' : '🎓 Войти через Moodle'}
|
||
</button>
|
||
</div>
|
||
<p class="link">
|
||
Нет аккаунта Moodle?
|
||
<a href="https://moodle-production-c39f.up.railway.app/login/signup.php"
|
||
target="_blank">Зарегистрироваться в Moodle</a>
|
||
</p>
|
||
{/if}
|
||
|
||
{#if error}
|
||
<div class="error">{error}</div>
|
||
{/if}
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.page {
|
||
min-height:100vh;
|
||
display:flex;
|
||
align-items:center;
|
||
justify-content:center;
|
||
background:#f5f7fa;
|
||
}
|
||
|
||
.card {
|
||
position:relative;
|
||
background:white;
|
||
border-radius:12px;
|
||
padding:40px;
|
||
width:100%;
|
||
max-width:420px;
|
||
box-shadow:0 4px 20px rgba(0,0,0,.1);
|
||
}
|
||
|
||
.close-btn {
|
||
position:absolute;
|
||
top:14px;
|
||
right:14px;
|
||
font-size:1.1em;
|
||
color:#aaa;
|
||
text-decoration:none;
|
||
padding:4px 8px;
|
||
border-radius:50%;
|
||
transition:all .2s;
|
||
}
|
||
|
||
.close-btn:hover {
|
||
color:#555;
|
||
background:#f0f0f0;
|
||
}
|
||
|
||
h1 {
|
||
margin:0 0 24px;
|
||
color:#1565c0;
|
||
text-align:center;
|
||
}
|
||
|
||
.tabs {
|
||
display:flex;
|
||
border-bottom:2px solid #e0e0e0;
|
||
margin-bottom:24px;
|
||
}
|
||
|
||
.tab {
|
||
flex:1;
|
||
padding:10px;
|
||
border:none;
|
||
background:none;
|
||
cursor:pointer;
|
||
color:#888;
|
||
font-size:.95em;
|
||
border-bottom:2px solid transparent;
|
||
margin-bottom:-2px;
|
||
transition:all .2s;
|
||
}
|
||
|
||
.tab.active {
|
||
color:#1565c0;
|
||
border-bottom-color:#1565c0;
|
||
font-weight:bold;
|
||
}
|
||
|
||
.form {
|
||
display:flex;
|
||
flex-direction:column;
|
||
gap:12px;
|
||
}
|
||
|
||
input {
|
||
width:100%;
|
||
padding:11px 14px;
|
||
border:1px solid #ddd;
|
||
border-radius:8px;
|
||
font-size:1em;
|
||
outline:none;
|
||
box-sizing:border-box;
|
||
}
|
||
|
||
input:focus {
|
||
border-color:#2196F3;
|
||
}
|
||
|
||
.pass-wrap {
|
||
position:relative;
|
||
display:flex;
|
||
align-items:center;
|
||
}
|
||
|
||
.pass-wrap input {
|
||
padding-right:44px;
|
||
}
|
||
|
||
.eye {
|
||
position:absolute;
|
||
right:10px;
|
||
background:none;
|
||
border:none;
|
||
cursor:pointer;
|
||
font-size:1.1em;
|
||
padding:4px;
|
||
}
|
||
|
||
.btn-primary {
|
||
background:#2196F3;
|
||
color:white;
|
||
border:none;
|
||
padding:12px;
|
||
border-radius:8px;
|
||
cursor:pointer;
|
||
font-size:1em;
|
||
font-weight:500;
|
||
}
|
||
|
||
.btn-primary:hover:not(:disabled) {
|
||
background:#1565c0;
|
||
}
|
||
|
||
.btn-moodle {
|
||
background:#f98012;
|
||
color:white;
|
||
border:none;
|
||
padding:12px;
|
||
border-radius:8px;
|
||
cursor:pointer;
|
||
font-size:1em;
|
||
font-weight:500;
|
||
}
|
||
|
||
.btn-moodle:hover:not(:disabled) {
|
||
background:#e06b00;
|
||
}
|
||
|
||
button:disabled {
|
||
opacity:.6;
|
||
cursor:default;
|
||
}
|
||
|
||
.hint {
|
||
font-size:.85em;
|
||
color:#888;
|
||
margin:0;
|
||
text-align:center;
|
||
}
|
||
|
||
.link {
|
||
text-align:center;
|
||
margin-top:16px;
|
||
font-size:.9em;
|
||
color:#666;
|
||
}
|
||
|
||
.link a {
|
||
color:#2196F3;
|
||
text-decoration:none;
|
||
}
|
||
|
||
.error {
|
||
background:#ffebee;
|
||
color:#c62828;
|
||
padding:10px 14px;
|
||
border-radius:8px;
|
||
margin-top:16px;
|
||
font-size:.9em;
|
||
text-align:center;
|
||
}
|
||
</style> |