Compare commits

..

No commits in common. "c7d443f2361de70e3170c57ca6b80cbafae1736a" and "210ac87ede81c8e742b0dfcbeeafd3d7cb4fc16c" have entirely different histories.

25 changed files with 25 additions and 548 deletions

5
.gitignore vendored
View file

@ -10,7 +10,6 @@ target/
*.jar *.jar
*.war *.war
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar
!gradle/wrapper/gradle-wrapper.jar
# Environment # Environment
.env .env
@ -40,10 +39,6 @@ Thumbs.db
docker-compose.override.yml docker-compose.override.yml
certs/ certs/
# Gradle
.gradle/
build/
# Java # Java
*.hprof *.hprof

View file

@ -24,23 +24,11 @@ PostgreSQL 16. Deployed via Docker Compose.
Always run these after making changes to verify nothing is broken. Always run these after making changes to verify nothing is broken.
Gradle lives at repo root. All commands below run from the repo root unless noted.
### Quick start (everything) ### Quick start (everything)
```bash ```bash
cp .env.example .env # first time only, then fill in keys cp .env.example .env # first time only, then fill in keys
docker compose up -d # starts postgres, backend, frontend docker compose up -d # starts postgres, backend, frontend
./gradlew up # same as above (Gradle wrapper)
```
### All-in-one
```bash
./gradlew check # frontend lint → frontend test → backend test → integration test
./gradlew up # docker compose up -d
./gradlew down # docker compose down
./gradlew reset # docker compose down -v && docker compose up -d (full DB reset)
``` ```
### Frontend (Vue.js 3 + Vite) ### Frontend (Vue.js 3 + Vite)
@ -57,8 +45,10 @@ npm run test # vitest
### Backend (Spring Boot 4 + Java 21) ### Backend (Spring Boot 4 + Java 21)
```bash ```bash
./gradlew :backend:bootRun # dev server on :8080 cd backend
./gradlew :backend:test # JUnit 5 + Mockito (backend only) ./gradlew bootRun # dev server on :8080
./gradlew test # JUnit 5 + Mockito
./gradlew check # full verification including integration tests
``` ```
### Stripe webhooks (local testing) ### Stripe webhooks (local testing)
@ -90,8 +80,7 @@ bilhej/
│ │ ├── router/ # Vue Router config │ │ ├── router/ # Vue Router config
│ │ └── assets/ # Static files, CSS │ │ └── assets/ # Static files, CSS
│ └── ... │ └── ...
├── backend/ # Spring Boot 4 (Java 21) — Gradle subproject ├── backend/ # Spring Boot 4 (Java 21)
│ ├── build.gradle # Spring Boot plugin, Java deps, test config
│ ├── src/main/java/se/bilhalsning/ │ ├── src/main/java/se/bilhalsning/
│ │ ├── config/ # @Configuration classes │ │ ├── config/ # @Configuration classes
│ │ ├── controller/ # REST controllers │ │ ├── controller/ # REST controllers
@ -107,7 +96,7 @@ bilhej/
│ ├── application-docker.yml # docker profile (PostgreSQL) │ ├── application-docker.yml # docker profile (PostgreSQL)
│ └── db/migration/ # Flyway migrations │ └── db/migration/ # Flyway migrations
├── docker/ # Dockerfiles ├── docker/ # Dockerfiles
│ ├── backend.Dockerfile # dev: JDK 21 + gradle :backend:bootRun │ ├── backend.Dockerfile # dev: JDK 21 + gradle bootRun
│ ├── backend.prod.Dockerfile # prod: multi-stage (Gradle build → JRE Alpine, non-root) │ ├── backend.prod.Dockerfile # prod: multi-stage (Gradle build → JRE Alpine, non-root)
│ ├── frontend.Dockerfile # dev: Node 24 + vite dev server │ ├── frontend.Dockerfile # dev: Node 24 + vite dev server
│ ├── frontend.prod.Dockerfile # prod: multi-stage (Node build → nginx) │ ├── frontend.prod.Dockerfile # prod: multi-stage (Node build → nginx)
@ -115,11 +104,6 @@ bilhej/
│ └── entrypoint.sh # prod: self-signed cert generation │ └── entrypoint.sh # prod: self-signed cert generation
├── docker-compose.yml # dev: postgres + backend (bootRun) + frontend (Vite HMR) ├── docker-compose.yml # dev: postgres + backend (bootRun) + frontend (Vite HMR)
├── docker-compose.prod.yml # prod: multi-stage images, no source mounts, restart always ├── docker-compose.prod.yml # prod: multi-stage images, no source mounts, restart always
├── gradlew # Gradle wrapper (repo root)
├── gradle/
│ └── wrapper/
├── settings.gradle # rootProject.name + include 'backend'
├── build.gradle # convenience tasks: check, up, down, reset
├── .env.example ├── .env.example
├── AGENTS.md # This file ├── AGENTS.md # This file
├── README.md ├── README.md

View file

@ -34,7 +34,7 @@ The user enters a registration number, composes a letter (from a template or fre
git clone <repo-url> bilhej git clone <repo-url> bilhej
cd bilhej cd bilhej
cp .env.example .env # fill in your keys cp .env.example .env # fill in your keys
docker compose up -d # or: ./gradlew up docker compose up -d
``` ```
The app will be available at: The app will be available at:
@ -72,7 +72,7 @@ No CORS configuration needed in development — the browser never calls the back
**Spring profiles:** **Spring profiles:**
| Profile | Datasource | Use | | Profile | Datasource | Use |
|---------|-----------|-----| |---------|-----------|-----|
| default | H2 in-memory | Local IDE dev (`./gradlew :backend:bootRun`) | | default | H2 in-memory | Local IDE dev (`./gradlew bootRun`) |
| `docker` | PostgreSQL via `docker-compose.yml` | Docker Compose dev | | `docker` | PostgreSQL via `docker-compose.yml` | Docker Compose dev |
| `prod` | PostgreSQL (production config) | Deploy (`docker-compose.prod.yml`) | | `prod` | PostgreSQL (production config) | Deploy (`docker-compose.prod.yml`) |
@ -130,21 +130,17 @@ bilhej/
├── docker-compose.yml # dev: postgres + backend (bootRun) + frontend (Vite HMR) ├── docker-compose.yml # dev: postgres + backend (bootRun) + frontend (Vite HMR)
├── docker-compose.prod.yml # prod: multi-stage builds, no source mounts, restart: unless-stopped ├── docker-compose.prod.yml # prod: multi-stage builds, no source mounts, restart: unless-stopped
├── docker/ ├── docker/
│ ├── backend.Dockerfile # dev: JDK + gradle :backend:bootRun │ ├── backend.Dockerfile # dev: JDK + gradle bootRun
│ ├── backend.prod.Dockerfile # prod: multi-stage (Gradle → JRE Alpine, non-root) │ ├── backend.prod.Dockerfile # prod: multi-stage (Gradle → JRE Alpine, non-root)
│ ├── frontend.Dockerfile # dev: Node + vite dev server │ ├── frontend.Dockerfile # dev: Node + vite dev server
│ ├── frontend.prod.Dockerfile # prod: multi-stage (Node → nginx) │ ├── frontend.prod.Dockerfile # prod: multi-stage (Node → nginx)
│ ├── nginx.conf # prod: SPA fallback + /api proxy │ ├── nginx.conf # prod: SPA fallback + /api proxy
│ └── entrypoint.sh # prod: self-signed cert generation │ └── entrypoint.sh # prod: self-signed cert generation
├── gradlew # Gradle wrapper (run from repo root)
├── gradle/
│ └── wrapper/
├── settings.gradle # rootProject.name + include 'backend'
├── build.gradle # convenience tasks: check, up, down, reset
├── .env.example ├── .env.example
├── README.md ├── README.md
├── REQUIREMENTS.md ├── REQUIREMENTS.md
└── CODING_GUIDELINES.md ├── CODING_GUIDELINES.md
└── ARCHITECTURE.md
``` ```
--- ---
@ -153,7 +149,7 @@ bilhej/
| Aspect | `docker compose up -d` | `docker compose -f docker-compose.prod.yml up -d` | | Aspect | `docker compose up -d` | `docker compose -f docker-compose.prod.yml up -d` |
|--------|------------------------|---------------------------------------------------| |--------|------------------------|---------------------------------------------------|
| Backend | `./gradlew :backend:bootRun` (compiles on change) | Multi-stage build → `java -jar app.jar` | | Backend | `./gradlew bootRun` (compiles on change) | Multi-stage build → `java -jar app.jar` |
| Backend image | `eclipse-temurin:21-jdk` (~400 MB) | `eclipse-temurin:21-jre-alpine` (~200 MB) | | Backend image | `eclipse-temurin:21-jdk` (~400 MB) | `eclipse-temurin:21-jre-alpine` (~200 MB) |
| Backend user | root | `bilhej` (non-root) | | Backend user | root | `bilhej` (non-root) |
| Frontend | Vite dev server (HMR, `--host 0.0.0.0`) | nginx serving static `dist/` | | Frontend | Vite dev server (HMR, `--host 0.0.0.0`) | nginx serving static `dist/` |
@ -167,27 +163,19 @@ bilhej/
## Development ## Development
### All-in-one (from repo root)
```bash
./gradlew check # lint → frontend test → backend test → integration test
./gradlew up # docker compose up -d
./gradlew down # docker compose down
./gradlew reset # docker compose down -v && docker compose up -d (full DB reset)
```
### Frontend (dev server with HMR) ### Frontend (dev server with HMR)
```bash ```bash
cd frontend cd frontend
npm install # first time only npm install
npm run dev # :3000 with HMR npm run dev
``` ```
### Backend (IDE or CLI) ### Backend (IDE or CLI)
```bash ```bash
./gradlew :backend:bootRun # :8080, profile: default (H2) cd backend
./gradlew bootRun
``` ```
### Stripe Webhooks (local testing) ### Stripe Webhooks (local testing)
@ -196,15 +184,10 @@ npm run dev # :3000 with HMR
stripe listen --forward-to localhost:8080/api/webhooks/stripe stripe listen --forward-to localhost:8080/api/webhooks/stripe
``` ```
### Database reset
```bash
./gradlew reset # wipes DB volume and restarts containers
```
--- ---
## Related Documents ## Related Documents
- [REQUIREMENTS.md](./REQUIREMENTS.md) — Full product requirements and business model - [REQUIREMENTS.md](./REQUIREMENTS.md) — Full product requirements and business model
- [CODING_GUIDELINES.md](./CODING_GUIDELINES.md) — Code conventions and standards - [CODING_GUIDELINES.md](./CODING_GUIDELINES.md) — Code conventions and standards
- [ARCHITECTURE.md](./ARCHITECTURE.md) — Detailed architecture and data flow

View file

View file

@ -1,3 +1 @@
rootProject.name = 'bilhej' rootProject.name = 'bilhej'
include 'backend'

View file

@ -1,38 +0,0 @@
plugins {
id 'base'
}
tasks.register('frontendLint', Exec) {
description = 'Run ESLint in the frontend directory'
workingDir = file("${rootProject.projectDir}/frontend")
commandLine 'npm', 'run', 'lint'
}
tasks.register('frontendTest', Exec) {
description = 'Run Vitest in the frontend directory'
dependsOn frontendLint
workingDir = file("${rootProject.projectDir}/frontend")
commandLine 'npm', 'run', 'test'
}
tasks.named('check').configure {
dependsOn frontendLint, frontendTest
}
tasks.register('up', Exec) {
description = 'Start all services via Docker Compose'
workingDir = rootProject.projectDir
commandLine 'docker', 'compose', 'up', '-d'
}
tasks.register('down', Exec) {
description = 'Stop all Docker Compose services'
workingDir = rootProject.projectDir
commandLine 'docker', 'compose', 'down'
}
tasks.register('reset', Exec) {
description = 'Wipe database and restart all services'
workingDir = rootProject.projectDir
commandLine 'bash', '-c', 'docker compose down -v && docker compose up -d'
}

View file

@ -36,7 +36,7 @@ services:
postgres: postgres:
condition: service_healthy condition: service_healthy
volumes: volumes:
- .:/app - ./backend:/app
- gradle-cache:/root/.gradle - gradle-cache:/root/.gradle
frontend: frontend:

View file

@ -1,3 +1,3 @@
FROM eclipse-temurin:21-jdk FROM eclipse-temurin:21-jdk
WORKDIR /app WORKDIR /app
ENTRYPOINT ["./gradlew", ":backend:bootRun", "--no-daemon"] ENTRYPOINT ["./gradlew", "bootRun", "--no-daemon"]

View file

@ -1,19 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router'
import AppHeader from '@/components/AppHeader.vue'
import AppFooter from '@/components/AppFooter.vue'
</script> </script>
<template> <template>
<AppHeader />
<main class="app__main">
<RouterView /> <RouterView />
</main>
<AppFooter />
</template> </template>
<style>
.app__main {
min-height: calc(100vh - 12rem);
}
</style>

View file

@ -1,10 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import AboutPage from '@/pages/AboutPage.vue'
describe('AboutPage', () => {
it('renders heading', () => {
const wrapper = mount(AboutPage)
expect(wrapper.text()).toContain('Om BilHälsning')
})
})

View file

@ -1,23 +1,9 @@
import { describe, it, expect } from 'vitest' import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import App from '@/App.vue' import App from '@/App.vue'
import AppHeader from '@/components/AppHeader.vue'
import AppFooter from '@/components/AppFooter.vue'
import router from '@/router' import router from '@/router'
describe('App', () => { describe('App', () => {
it('renders AppHeader and AppFooter', async () => {
router.push('/')
await router.isReady()
const wrapper = mount(App, {
global: {
plugins: [router],
},
})
expect(wrapper.findComponent(AppHeader).exists()).toBe(true)
expect(wrapper.findComponent(AppFooter).exists()).toBe(true)
})
it('renders RouterView with HomePage content', async () => { it('renders RouterView with HomePage content', async () => {
router.push('/') router.push('/')
await router.isReady() await router.isReady()
@ -26,6 +12,6 @@ describe('App', () => {
plugins: [router], plugins: [router],
}, },
}) })
expect(wrapper.text()).toContain('Skicka ett brev till en fordonsägare') expect(wrapper.text()).toContain('BilHälsning')
}) })
}) })

View file

@ -1,54 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { createRouter, createMemoryHistory } from 'vue-router'
import AppFooter from '@/components/AppFooter.vue'
function createTestRouter() {
return createRouter({
history: createMemoryHistory(),
routes: [
{
path: '/om',
name: 'about',
component: { template: '<div>About</div>' },
},
{
path: '/kontakt',
name: 'contact',
component: { template: '<div>Contact</div>' },
},
{
path: '/integritetspolicy',
name: 'privacy',
component: { template: '<div>Privacy</div>' },
},
{
path: '/villkor',
name: 'terms',
component: { template: '<div>Terms</div>' },
},
],
})
}
describe('AppFooter', () => {
it('renders all four links', () => {
const router = createTestRouter()
const wrapper = mount(AppFooter, {
global: { plugins: [router] },
})
const links = wrapper.findAll('a')
expect(links[0].text()).toBe('Om oss')
expect(links[0].attributes('href')).toBe('/om')
expect(links[1].text()).toBe('Kontakt')
expect(links[1].attributes('href')).toBe('/kontakt')
expect(links[2].text()).toBe('Integritetspolicy')
expect(links[2].attributes('href')).toBe('/integritetspolicy')
expect(links[3].text()).toBe('Villkor')
expect(links[3].attributes('href')).toBe('/villkor')
})
})

View file

@ -1,33 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { createRouter, createMemoryHistory } from 'vue-router'
import AppHeader from '@/components/AppHeader.vue'
function createTestRouter() {
return createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/', name: 'home', component: { template: '<div>Home</div>' } },
],
})
}
describe('AppHeader', () => {
it('renders the logo text', () => {
const router = createTestRouter()
const wrapper = mount(AppHeader, {
global: { plugins: [router] },
})
expect(wrapper.text()).toContain('BilHälsning')
})
it('has a link to home', () => {
const router = createTestRouter()
const wrapper = mount(AppHeader, {
global: { plugins: [router] },
})
const links = wrapper.findAll('a')
const homeLink = links.find((a) => a.attributes('href') === '/')
expect(homeLink).toBeTruthy()
})
})

View file

@ -1,43 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { createRouter, createMemoryHistory } from 'vue-router'
import ComposePage from '@/pages/ComposePage.vue'
function createTestRouter() {
return createRouter({
history: createMemoryHistory(),
routes: [{ path: '/compose', name: 'compose', component: ComposePage }],
})
}
describe('ComposePage', () => {
it('renders heading', async () => {
const router = createTestRouter()
router.push('/compose')
await router.isReady()
const wrapper = mount(ComposePage, {
global: { plugins: [router] },
})
expect(wrapper.text()).toContain('Skriv ditt brev')
})
it('displays plate from query param', async () => {
const router = createTestRouter()
router.push({ path: '/compose', query: { plate: 'ABC123' } })
await router.isReady()
const wrapper = mount(ComposePage, {
global: { plugins: [router] },
})
expect(wrapper.text()).toContain('ABC123')
})
it('does not show plate when no query param', async () => {
const router = createTestRouter()
router.push('/compose')
await router.isReady()
const wrapper = mount(ComposePage, {
global: { plugins: [router] },
})
expect(wrapper.find('.compose__plate').exists()).toBe(false)
})
})

View file

@ -1,10 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import ContactPage from '@/pages/ContactPage.vue'
describe('ContactPage', () => {
it('renders heading', () => {
const wrapper = mount(ContactPage)
expect(wrapper.text()).toContain('Kontakta oss')
})
})

View file

@ -1,85 +1,10 @@
import { describe, it, expect } from 'vitest' import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { createRouter, createMemoryHistory } from 'vue-router'
import HomePage from '@/pages/HomePage.vue' import HomePage from '@/pages/HomePage.vue'
import ComposePage from '@/pages/ComposePage.vue'
function createTestRouter() {
return createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/', name: 'home', component: HomePage },
{ path: '/compose', name: 'compose', component: ComposePage },
],
})
}
function mountHome(router: ReturnType<typeof createTestRouter>) {
return mount(HomePage, {
global: { plugins: [router] },
})
}
describe('HomePage', () => { describe('HomePage', () => {
it('renders subtitle', () => { it('mounts successfully', () => {
const router = createTestRouter() const wrapper = mount(HomePage)
const wrapper = mountHome(router) expect(wrapper.text()).toContain('BilHälsning')
expect(wrapper.text()).toContain('Skicka ett brev till en fordonsägare')
})
it('does not show CTA button initially', () => {
const router = createTestRouter()
const wrapper = mountHome(router)
expect(wrapper.find('.home__cta').exists()).toBe(false)
})
it('does not show CTA while loading', async () => {
const router = createTestRouter()
const wrapper = mountHome(router)
const plateInput = wrapper.findComponent({ name: 'PlateInput' })
await plateInput.vm.$emit('lookup', 'ABC123')
await wrapper.vm.$nextTick()
expect(wrapper.find('.home__cta').exists()).toBe(false)
})
it('does not show CTA after not-found', async () => {
const router = createTestRouter()
const wrapper = mountHome(router)
const plateInput = wrapper.findComponent({ name: 'PlateInput' })
await plateInput.vm.$emit('lookup', 'UNKNOWN')
await new Promise((resolve) => setTimeout(resolve, 500))
expect(wrapper.find('.home__cta').exists()).toBe(false)
})
it('shows CTA button when vehicle data present', async () => {
const router = createTestRouter()
const wrapper = mountHome(router)
const plateInput = wrapper.findComponent({ name: 'PlateInput' })
await plateInput.vm.$emit('update:modelValue', 'ABC123')
await plateInput.vm.$emit('lookup', 'ABC123')
await new Promise((resolve) => setTimeout(resolve, 500))
const cta = wrapper.find('.home__cta')
expect(cta.exists()).toBe(true)
expect(cta.text()).toBe('Skicka ett brev till ägaren')
})
it('CTA links to compose page with plate query param', async () => {
const router = createTestRouter()
const wrapper = mountHome(router)
const plateInput = wrapper.findComponent({ name: 'PlateInput' })
await plateInput.vm.$emit('update:modelValue', 'ABC123')
await plateInput.vm.$emit('lookup', 'ABC123')
await new Promise((resolve) => setTimeout(resolve, 500))
const cta = wrapper.find('.home__cta')
const href = cta.attributes('href')
expect(href).toBe('/compose?plate=ABC123')
}) })
}) })

View file

@ -1,42 +0,0 @@
<script setup lang="ts">
import { RouterLink } from 'vue-router'
</script>
<template>
<footer class="app-footer">
<nav class="app-footer__links">
<RouterLink to="/om" class="app-footer__link">Om oss</RouterLink>
<RouterLink to="/kontakt" class="app-footer__link">Kontakt</RouterLink>
<RouterLink to="/integritetspolicy" class="app-footer__link"
>Integritetspolicy</RouterLink
>
<RouterLink to="/villkor" class="app-footer__link">Villkor</RouterLink>
</nav>
</footer>
</template>
<style scoped>
.app-footer {
background: #f7fafc;
border-top: 1px solid #e2e8f0;
padding: 1.5rem;
text-align: center;
}
.app-footer__links {
display: flex;
justify-content: center;
gap: 2rem;
flex-wrap: wrap;
}
.app-footer__link {
color: #718096;
text-decoration: none;
font-size: 0.8125rem;
}
.app-footer__link:hover {
color: #1a202c;
}
</style>

View file

@ -1,45 +0,0 @@
<script setup lang="ts">
import { RouterLink } from 'vue-router'
</script>
<template>
<header class="app-header">
<RouterLink to="/" class="app-header__logo">BilHälsning</RouterLink>
<nav class="app-header__nav">
<RouterLink to="/" class="app-header__link">Hem</RouterLink>
</nav>
</header>
</template>
<style scoped>
.app-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 1.5rem;
border-bottom: 1px solid #e2e8f0;
background: #fff;
}
.app-header__logo {
font-size: 1.25rem;
font-weight: 700;
color: #1a202c;
text-decoration: none;
}
.app-header__nav {
display: flex;
gap: 1rem;
}
.app-header__link {
color: #4a5568;
text-decoration: none;
font-size: 0.875rem;
}
.app-header__link:hover {
color: #1a202c;
}
</style>

View file

@ -1,19 +0,0 @@
<script setup lang="ts"></script>
<template>
<div class="about">
<h1>Om BilHälsning</h1>
<p>
BilHälsning är en tjänst som låter dig skicka fysiska brev till
fordonsägare via registreringsnummer.
</p>
</div>
</template>
<style scoped>
.about {
max-width: 28rem;
margin: 3rem auto 0;
padding: 0 1rem;
}
</style>

View file

@ -1,26 +0,0 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
const plate = (route.query.plate as string) || ''
</script>
<template>
<div class="compose">
<h1>Skriv ditt brev</h1>
<p v-if="plate" class="compose__plate">Registreringsnummer: {{ plate }}</p>
</div>
</template>
<style scoped>
.compose {
max-width: 28rem;
margin: 3rem auto 0;
padding: 0 1rem;
}
.compose__plate {
color: #4a5568;
font-size: 0.875rem;
}
</style>

View file

@ -1,19 +0,0 @@
<script setup lang="ts"></script>
<template>
<div class="contact">
<h1>Kontakta oss</h1>
<p>
Har du frågor eller feedback? Hör av dig till oss
<a href="mailto:hej@bilhalsning.se">hej@bilhalsning.se</a>.
</p>
</div>
</template>
<style scoped>
.contact {
max-width: 28rem;
margin: 3rem auto 0;
padding: 0 1rem;
}
</style>

View file

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { RouterLink } from 'vue-router'
import PlateInput from '@/components/PlateInput.vue' import PlateInput from '@/components/PlateInput.vue'
import VehicleInfo from '@/components/VehicleInfo.vue' import VehicleInfo from '@/components/VehicleInfo.vue'
import type { VehicleInfo as VehicleData } from '@/components/VehicleInfo.vue' import type { VehicleInfo as VehicleData } from '@/components/VehicleInfo.vue'
@ -37,6 +36,7 @@ function handleLookup(lookedUpPlate: string) {
<template> <template>
<div class="home"> <div class="home">
<h1>BilHälsning</h1>
<p class="home__subtitle">Skicka ett brev till en fordonsägare</p> <p class="home__subtitle">Skicka ett brev till en fordonsägare</p>
<PlateInput v-model="plate" @lookup="handleLookup" /> <PlateInput v-model="plate" @lookup="handleLookup" />
@ -47,14 +47,6 @@ function handleLookup(lookedUpPlate: string) {
:not-found="notFound" :not-found="notFound"
:plate="plate" :plate="plate"
/> />
<RouterLink
v-if="vehicle"
:to="{ name: 'compose', query: { plate } }"
class="home__cta"
>
Skicka ett brev till ägaren
</RouterLink>
</div> </div>
</template> </template>
@ -69,21 +61,4 @@ function handleLookup(lookedUpPlate: string) {
color: #718096; color: #718096;
margin: 0 0 1.5rem 0; margin: 0 0 1.5rem 0;
} }
.home__cta {
display: block;
margin-top: 1.5rem;
padding: 0.875rem 1.5rem;
background: #38a169;
color: #fff;
text-align: center;
text-decoration: none;
font-weight: 600;
border-radius: 0.5rem;
font-size: 1rem;
}
.home__cta:hover {
background: #2f855a;
}
</style> </style>

View file

@ -1,8 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import HomePage from '@/pages/HomePage.vue' import HomePage from '@/pages/HomePage.vue'
import ComposePage from '@/pages/ComposePage.vue'
import AboutPage from '@/pages/AboutPage.vue'
import ContactPage from '@/pages/ContactPage.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@ -12,21 +9,6 @@ const router = createRouter({
name: 'home', name: 'home',
component: HomePage, component: HomePage,
}, },
{
path: '/compose',
name: 'compose',
component: ComposePage,
},
{
path: '/om',
name: 'about',
component: AboutPage,
},
{
path: '/kontakt',
name: 'contact',
component: ContactPage,
},
], ],
}) })