diff --git a/src/app.html b/src/app.html index 3ba5379..2c70eb0 100644 --- a/src/app.html +++ b/src/app.html @@ -17,6 +17,9 @@ diff --git a/src/lib/api/profiles.ts b/src/lib/api/profiles.ts new file mode 100644 index 0000000..538c42d --- /dev/null +++ b/src/lib/api/profiles.ts @@ -0,0 +1,19 @@ +/* API functions for SavedFlightProfile */ +import type {SavedFlightProfile } from "$lib/types"; +import { getAPI, postAPI, putAPI, deleteAPI } from "./base"; + +export function getSavedFlightProfiles(): Promise { + return getAPI("/saved-profiles/"); +} + +export function saveFlightProfile(profile: SavedFlightProfile): Promise { + return postAPI("/saved-profiles/", profile); +} + +export function updateFlightProfile(profile: SavedFlightProfile): Promise { + return putAPI(`/saved-profiles/${profile.id}/`, profile); +} + +export function deleteFlightProfile(id: number): Promise { + return deleteAPI(`/saved-profiles/${id}/`); +} \ No newline at end of file diff --git a/src/lib/api/templates.ts b/src/lib/api/templates.ts new file mode 100644 index 0000000..01d51b6 --- /dev/null +++ b/src/lib/api/templates.ts @@ -0,0 +1,19 @@ +/* API functions for SavedScenarioTemplate */ +import type { SavedScenarioTemplate } from "$lib/types"; +import { getAPI, postAPI, putAPI, deleteAPI } from "./base"; + +export function getSavedScenarioTemplates(): Promise { + return getAPI("/saved-templates/"); +} + +export function saveScenarioTemplate(template: SavedScenarioTemplate): Promise { + return postAPI("/saved-templates/", template); +} + +export function updateScenarioTemplate(template: SavedScenarioTemplate): Promise { + return putAPI(`/saved-templates/${template.id}/`, template); +} + +export function deleteScenarioTemplate(id: number): Promise { + return deleteAPI(`/saved-templates/${id}/`); +} \ No newline at end of file diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 8d8bebb..b222090 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -5,76 +5,129 @@ export const LOGIN_URL = 'http://localhost:8000/api/login/'; export const LOGOUT_URL = 'http://localhost:8000/api/logout/'; export const SESSION_URL = 'http://localhost:8000/api/session/'; export const WHOAMI_URL = 'http://localhost:8000/api/whoami/'; - export async function getCsrfToken(): Promise { return Cookies.get('csrftoken') || null; } export async function getCsrfTokenAuth(): Promise { - const response = await fetch(CSRF_URL, {}); - console.log('CSRF Token Response:', response); - return Cookies.get('csrftoken') || null; + try { + await fetch(CSRF_URL, {}); + return Cookies.get('csrftoken') || null; + } catch (error) { + console.error('Failed to get CSRF token:', error); + return Promise.reject(error); + } } export async function checkAuthenticated(): Promise { - const csrfToken = await getCsrfTokenAuth(); - if (!csrfToken) { - throw new Error('CSRF token not found'); + try { + const csrfToken = await getCsrfTokenAuth(); + if (!csrfToken) { + throw new Error('CSRF token not found'); + } + const response = await fetch(SESSION_URL, { + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrfToken + }, + }); + if (!response.ok) { + throw new Error(`Authentication check failed: ${response.statusText}`); + } + const data = await response.json(); + return data.isAuthenticated; + } catch (error) { + console.error('Authentication check failed:', error); + return Promise.reject(error); } - const response = await fetch(SESSION_URL, { - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': csrfToken - }, - }); - let data = await (response as Response).json(); - return data.isAuthenticated; } -export async function login(username: string, password: string): Promise { - const csrfToken = await getCsrfTokenAuth(); - if (!csrfToken) { - throw new Error('CSRF token not found'); +export async function login(username: string, password: string): Promise { + try { + const csrfToken = await getCsrfTokenAuth(); + if (!csrfToken) { + throw new Error('CSRF token not found'); + } + + const response = await fetch(LOGIN_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrfToken + }, + body: JSON.stringify({ username, password }), + credentials: 'include' + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(`Login failed: ${response.statusText} - ${errorData.detail || ''}`); + } + + return await response.json(); + } catch (error) { + console.error('Login failed:', error); + return Promise.reject(error); } - - const response = await fetch(LOGIN_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': csrfToken - }, - body: JSON.stringify({ username, password }), - credentials: 'include' - }); - - if (!response.ok) { - throw new Error(`Login failed: ${response.statusText}`); - } - - const data = await response.json(); - return data; } export async function logout(): Promise { - const csrfToken = await getCsrfTokenAuth(); - if (!csrfToken) { - throw new Error('CSRF token not found'); + try { + const csrfToken = await getCsrfTokenAuth(); + if (!csrfToken) { + throw new Error('CSRF token not found'); + } + + const response = await fetch(LOGOUT_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrfToken + }, + credentials: 'include' + }); + + if (!response.ok) { + throw new Error(`Logout failed: ${response.statusText}`); + } + + console.log('Logout successful'); + } catch (error) { + console.error('Logout failed:', error); + return Promise.reject(error); } +} - const response = await fetch(LOGOUT_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': csrfToken - }, - credentials: 'include' - }); +export async function whoami(): Promise { + try { + const csrfToken = await getCsrfTokenAuth(); + if (!csrfToken) { + throw new Error('CSRF token not found'); + } - if (!response.ok) { - throw new Error(`Logout failed: ${response.statusText}`); + const response = await fetch(WHOAMI_URL, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrfToken + }, + credentials: 'include' + }); + + if (!response.ok) { + throw new Error(`Whoami failed: ${response.statusText}`); + } + + const data = await response.json(); + + if (!data || !data.username) { + throw new Error('No user data found'); + } + + return data.username; + } catch (error) { + console.error('Whoami failed:', error); + return Promise.reject(error); } - - console.log('Logout successful'); - return; } \ No newline at end of file diff --git a/src/lib/components/ConfirmationPrompt.svelte b/src/lib/components/ConfirmationPrompt.svelte new file mode 100644 index 0000000..3808413 --- /dev/null +++ b/src/lib/components/ConfirmationPrompt.svelte @@ -0,0 +1,44 @@ + + + + {title} + + {#if children} + {@render children()} + {:else} + Вы действительно хотите продолжить? + {/if} + + + + + + \ No newline at end of file diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte new file mode 100644 index 0000000..4cb9911 --- /dev/null +++ b/src/lib/components/Footer.svelte @@ -0,0 +1,36 @@ + + \ No newline at end of file diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 77d30f0..41772eb 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -1,7 +1,8 @@ - @@ -70,30 +76,31 @@ diff --git a/src/lib/components/PointListModal.svelte b/src/lib/components/PointListModal.svelte index 8e7e7ad..51e0e34 100644 --- a/src/lib/components/PointListModal.svelte +++ b/src/lib/components/PointListModal.svelte @@ -1,6 +1,7 @@ -
+
-
-

User Account

-

Manage your account settings here.

- +
+ +
+
+ + + + +
+ + + +
Основная информация
+
+ +
+
+ + + + +
+
+ + + + +
+
+ + + + + {#if editMode} + + + {:else} + + {/if} +
+
+ + + + +
Смена пароля
+
+ + + + + +
+
+ + + + +
+
+ + + + +
+
+ +
+
+ + + + +
Токен API
+
+ + + + +
+ + +
+ +
+
+ +
+
+ + + + +
Действия с аккаунтом
+
+ +
+ + + + + +
+
+
+
+
-
\ No newline at end of file +
+
+ + + (showConfirm = false)} +> +

{confirmConfig.body}

+
diff --git a/src/routes/user/templates/+page.svelte b/src/routes/user/templates/+page.svelte new file mode 100644 index 0000000..b1948f8 --- /dev/null +++ b/src/routes/user/templates/+page.svelte @@ -0,0 +1,345 @@ + + +
+ +
+ +
+
+ + + + +
+ + + +
Точки запуска
+
+ + pointsSearch.set()} + /> +
+ + + + + + + + + + + + {#each pointsTable.rows as row} + + + + + + + + {/each} + +
НазваниеШиротаДолготаВысота
{row.name}{row.lat.toFixed(4)} °{row.lon.toFixed(4)} °{row.alt} м + +
+
+ + + pointsTable.setPage("previous")} /> + + {#each pointsTable.pagesWithEllipsis as page} + + pointsTable.setPage(page)}>{page} + + {/each} + + pointsTable.setPage("next")} /> + + +
+
+ + + + +
Профили полета
+
+ + profilesSearch.set()} + /> +
+ + + + + + + + + + + + {#each profilesTable.rows as row} + + + + + + + + {/each} + +
НазваниеСкороподъемностьСкорость сниженияВысота разрыва
{row.name}{row.rate_profile_data.ascent_rate} м/с{row.rate_profile_data.descent_rate} м/с{row.rate_profile_data.burst_altitude} м + +
+
+ + + profilesTable.setPage("previous")} /> + + {#each profilesTable.pagesWithEllipsis as page} + + profilesTable.setPage(page)}>{page} + + {/each} + + profilesTable.setPage("next")} /> + + +
+
+ + + + +
Сценарии
+
+ + templatesSearch.set()} + /> +
+ + + + + + + + + + {#each templatesTable.rows as row} + + + + + + {/each} + +
НазваниеОписание
{row.name}{row.template_data.description} + +
+
+ + + templatesTable.setPage("previous")} /> + + {#each templatesTable.pagesWithEllipsis as page} + + templatesTable.setPage(page)}>{page} + + {/each} + + templatesTable.setPage("next")} /> + + +
+
+
+
+
+
+
+ + (showConfirm = false)} +> +

{confirmConfig.body}

+
\ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index c118b4c..41fa26e 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -59,7 +59,6 @@ position: relative; width: 100%; height: calc(100vh - var(--navbar-height)); - top: var(--navbar-height); } .coordinates-display { @@ -123,6 +122,12 @@ width: 1%; } +.force-page-height { + min-height: 100vh; + display: flex; + flex-direction: column; +} + @media (max-width: 767.98px) { .coordinates-display { diff --git a/static/logo-full-ru-dark.svg b/static/logo-full-ru-dark.svg new file mode 100644 index 0000000..a8a41c2 --- /dev/null +++ b/static/logo-full-ru-dark.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +