From ccf0130bba0e55dc0c77aa16d7bc4d2466afb797 Mon Sep 17 00:00:00 2001 From: Maxim Date: Tue, 16 Dec 2025 22:15:39 +0300 Subject: [PATCH] v2 --- .dockerignore | 6 + Dockerfile | 21 +++ nginx.conf | 23 +++ src/App.vue | 37 +++- src/pages/BuildOfTheDayPage.vue | 306 ++++++++++++++++++++++++++++++++ src/router/index.ts | 6 + src/stores/randomData.ts | 31 +++- 7 files changed, 427 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 nginx.conf create mode 100644 src/pages/BuildOfTheDayPage.vue diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e7b910d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +node_modules +dist +.git +.gitignore +*.log +.env* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..863844a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM node:22-alpine AS build + +WORKDIR /app + +# Install dependencies +COPY package*.json ./ +RUN npm ci + +# Copy source and build +COPY . . +RUN npm run build-only + +# Production stage with nginx +FROM nginx:alpine + +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..63656bf --- /dev/null +++ b/nginx.conf @@ -0,0 +1,23 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/ { + proxy_pass http://backend:8000/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} diff --git a/src/App.vue b/src/App.vue index b12d115..837b0f8 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,9 +1,13 @@ @@ -89,4 +93,35 @@ import { RouterView } from 'vue-router'; @media (max-width: 720px){ .container{ padding: 20px; } } + +/* Navigation */ +.nav { + display: flex; + gap: 12px; + margin-bottom: 24px; + justify-content: center; +} + +.nav-link { + padding: 10px 20px; + border-radius: 10px; + text-decoration: none; + color: #e6f0fa; + font-weight: 600; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.08); + transition: all 0.2s ease; +} + +.nav-link:hover { + background: rgba(255, 255, 255, 0.1); + box-shadow: 0 0 12px rgba(79, 195, 247, 0.2); +} + +.nav-link.router-link-exact-active { + background: linear-gradient(90deg, #b63a2b, #8b241b); + color: #f8e7c6; + border-color: rgba(182, 58, 43, 0.5); + box-shadow: 0 0 12px rgba(182, 58, 43, 0.45); +} diff --git a/src/pages/BuildOfTheDayPage.vue b/src/pages/BuildOfTheDayPage.vue new file mode 100644 index 0000000..e3042ae --- /dev/null +++ b/src/pages/BuildOfTheDayPage.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/src/router/index.ts b/src/router/index.ts index 38c3e65..4ceb4b6 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,4 +1,5 @@ import HomePage from '@/pages/HomePage.vue' +import BuildOfTheDayPage from '@/pages/BuildOfTheDayPage.vue' import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ @@ -8,6 +9,11 @@ const router = createRouter({ path: '/', name: 'HomePage', component: HomePage + }, + { + path: '/build-of-day', + name: 'BuildOfTheDayPage', + component: BuildOfTheDayPage } ] }); diff --git a/src/stores/randomData.ts b/src/stores/randomData.ts index 403471a..54d0438 100644 --- a/src/stores/randomData.ts +++ b/src/stores/randomData.ts @@ -21,6 +21,14 @@ export interface RandomizeResult { aspect?: string } +export interface BuildOfDayResult { + date: string + hero: Hero + items: Item[] + skillBuild: SkillBuild + aspect?: string +} + type RandomizePayload = { includeSkills: boolean includeAspect: boolean @@ -29,7 +37,7 @@ type RandomizePayload = { } const PREFS_KEY = 'randomizer:prefs' -const BASE_URL = 'http://127.0.0.1:8000' +const BASE_URL = import.meta.env.VITE_API_URL || '' export const useRandomDataStore = defineStore('randomData', { state: () => ({ @@ -42,7 +50,10 @@ export const useRandomDataStore = defineStore('randomData', { includeAspect: false, itemsCount: 6, heroId: null as number | null - } as RandomizePayload + } as RandomizePayload, + buildOfDay: null as BuildOfDayResult | null, + buildOfDayLoading: false, + buildOfDayError: null as string | null }), actions: { @@ -107,6 +118,22 @@ export const useRandomDataStore = defineStore('randomData', { } finally { this.loading = false } + }, + + async fetchBuildOfDay() { + if (this.buildOfDayLoading) return + this.buildOfDayLoading = true + this.buildOfDayError = null + + try { + const { data } = await axios.get(`${BASE_URL}/api/build-of-day`) + this.buildOfDay = data + } catch (err) { + const message = err instanceof Error ? err.message : 'Failed to load build of the day' + this.buildOfDayError = message + } finally { + this.buildOfDayLoading = false + } } } })