Compare commits
2 Commits
8ad6fda412
...
1e1e829e2d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e1e829e2d | ||
|
|
f686c4a7d2 |
26
package-lock.json
generated
26
package-lock.json
generated
@@ -7,17 +7,16 @@
|
||||
"": {
|
||||
"name": "minibusservice.no",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"leaflet": "^1.9.4",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"@tailwindcss/vite": "^0.0.0-insiders.aaaefe8",
|
||||
"@tsconfig/svelte": "^5.0.8",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/node": "^24.12.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"svelte": "^5.53.7",
|
||||
"svelte-check": "^4.4.5",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^0.0.0-insiders.aaaefe8",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^8.0.0"
|
||||
@@ -994,6 +993,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/leaflet": {
|
||||
"version": "1.9.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
|
||||
"integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
|
||||
@@ -1207,6 +1223,7 @@
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
@@ -1703,6 +1720,7 @@
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz",
|
||||
"integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
||||
@@ -13,15 +13,14 @@
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"@tailwindcss/vite": "^0.0.0-insiders.aaaefe8",
|
||||
"@tsconfig/svelte": "^5.0.8",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/node": "^24.12.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"svelte": "^5.53.7",
|
||||
"svelte-check": "^4.4.5",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^0.0.0-insiders.aaaefe8",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"leaflet": "^1.9.4",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,8 @@
|
||||
|
||||
<div class="mt-2 flex flex-col gap-8 md:flex-row md:text-lg">
|
||||
<div class="flex flex-1 flex-col">
|
||||
<p class="block pt-2 font-medium opacity-70 md:hidden">
|
||||
Spørsmål?<br />Kontakt oss via tlf, e-post eller via skjemaet under
|
||||
</p>
|
||||
<p class="hidden pt-2 font-medium opacity-70 md:block">
|
||||
Spørsmål?<br />Kontakt oss via tlf, e-post eller via skjemaet til
|
||||
høyre
|
||||
<p class="pt-2 font-medium opacity-70">
|
||||
Spørsmål?<br />Kontakt oss via tlf eller e-post
|
||||
</p>
|
||||
<Link
|
||||
class="w-fit font-bold text-accent hover:underline"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import L from "leaflet";
|
||||
|
||||
interface MarkerPoint {
|
||||
latitude: number;
|
||||
@@ -15,10 +16,8 @@
|
||||
let map: L.Map | null = null;
|
||||
let markers: L.Marker<any>[] = [];
|
||||
var icon: L.Icon;
|
||||
let L: typeof import("leaflet/index");
|
||||
|
||||
onMount(async () => {
|
||||
L = await import("leaflet");
|
||||
icon = L.icon({
|
||||
iconUrl: "/map_marker.png",
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Button from "./button/Button.svelte";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export let maxItems: number;
|
||||
|
||||
let pageParam = $page.url.searchParams.get("page");
|
||||
let currentPage = 1;
|
||||
if (pageParam) {
|
||||
let pageParamAsNumber = Number(pageParam);
|
||||
if (pageParamAsNumber) {
|
||||
currentPage = pageParamAsNumber;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<section
|
||||
class={twMerge("mt-8 flex justify-center gap-5", $$restProps["class"])}
|
||||
>
|
||||
{#if currentPage > 1}
|
||||
<a
|
||||
on:click={() =>
|
||||
setTimeout(() => {
|
||||
currentPage = currentPage - 1;
|
||||
}, 10)}
|
||||
href={`?page=${currentPage - 1}`}
|
||||
>
|
||||
<Button>Forrige</Button>
|
||||
</a>
|
||||
{/if}
|
||||
{#if (currentPage + 1) * 10 - 10 < maxItems}
|
||||
<a
|
||||
on:click={() =>
|
||||
setTimeout(() => {
|
||||
currentPage = currentPage + 1;
|
||||
}, 10)}
|
||||
href={`?page=${currentPage + 1}`}
|
||||
>
|
||||
<Button>Neste</Button>
|
||||
</a>
|
||||
{/if}
|
||||
</section>
|
||||
@@ -172,7 +172,9 @@
|
||||
let { notfound }: Props = $props();
|
||||
|
||||
onMount(() => {
|
||||
// @ts-ignore
|
||||
if (window.navigation) {
|
||||
// @ts-ignore
|
||||
function handleNavigate(event: NavigateEvent) {
|
||||
event.intercept({
|
||||
handler: async () => {
|
||||
@@ -181,9 +183,11 @@
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
window.navigation.addEventListener("navigate", handleNavigate);
|
||||
|
||||
return () => {
|
||||
// @ts-ignore
|
||||
window.navigation.removeEventListener(
|
||||
"navigate",
|
||||
handleNavigate,
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Link from './link/Link.svelte';
|
||||
</script>
|
||||
|
||||
<h2 class="mb-1 text-xl font-semibold">Innledning</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Dette kjøpet er regulert av de nedenstående standard salgsbetingelser for forbrukerkjøp av varer
|
||||
over Internett. Forbrukerkjøp over internett reguleres hovedsakelig av avtaleloven,
|
||||
forbrukerkjøpsloven, markedsføringsloven, angrerettloven og ehandelsloven, og disse lovene gir
|
||||
forbrukeren ufravikelige rettigheter. Lovene er tilgjengelig på
|
||||
<Link href="www.lovdata.no">www.lovdata.no</Link>. Vilkårene i denne avtalen skal ikke forstås som
|
||||
noen begrensning i de lovbestemte rettighetene, men oppstiller partenes viktigste rettigheter og
|
||||
plikter for handelen.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Salgsbetingelsene er utarbeidet og anbefalt av Forbrukertilsynet. For en bedre forståelse av disse
|
||||
salgsbetingelsene, se Forbrukertilsynets veileder her.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">1. Avtalen</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Avtalen består av disse salgsbetingelsene, opplysninger gitt i bestillingsløsningen og eventuelt
|
||||
særskilt avtalte vilkår. Ved eventuell motstrid mellom opplysningene, går det som særskilt er
|
||||
avtalt mellom partene foran, så fremt det ikke strider mot ufravikelig lovgivning.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Avtalen vil i tillegg bli utfylt av relevante lovbestemmelser som regulerer kjøp av varer mellom
|
||||
næringsdrivende og forbrukere.
|
||||
</p>
|
||||
|
||||
<h2 class="mb-1 text-xl font-semibold">2. Partene</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Selger er Tor Stian Stene, adresse: Solnørdalsvegen 40, 6240 Ørskog, e-post:
|
||||
minibusservice@hotmail.com, tlf: 45266161, org.nr: 816 230 942, og betegnes i det følgende som
|
||||
selgeren.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøper er den forbrukeren som foretar bestillingen, og betegnes i det følgende som kjøperen.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">3. Pris</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Den oppgitte prisen for varen og tjenester er den totale prisen kjøper skal betale. Denne prisen
|
||||
inkluderer alle avgifter og tilleggskostnader. Ytterligere kostnader som selger før kjøpet ikke
|
||||
har informert om, skal kjøper ikke bære.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">4. Avtaleinngåelse</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Avtalen er bindende for begge parter når kjøperen har sendt sin bestilling til selgeren.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Avtalen er likevel ikke bindende hvis det har forekommet skrive- eller tastefeil i tilbudet fra
|
||||
selgeren i bestillingsløsningen i nettbutikken eller i kjøperens bestilling, og den annen part
|
||||
innså eller burde ha innsett at det forelå en slik feil.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">5. Betalingen</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Selgeren kan kreve betaling for varen fra det tidspunkt den blir sendt fra selgeren til kjøperen.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom kjøperen bruker kredittkort eller debetkort ved betaling, kan selgeren reservere
|
||||
kjøpesummen på kortet ved bestilling. Kortet blir belastet samme dag som varen sendes.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Ved betaling med faktura, blir fakturaen til kjøperen utstedt ved forsendelse av varen.
|
||||
Betalingsfristen fremgår av fakturaen og er på minimum 14 dager fra mottak.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">Kjøpere under 18 år kan ikke betale med etterfølgende faktura.</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">6. Levering</h2>
|
||||
|
||||
<p class="mb-2">Levering er skjedd når kjøperen, eller hans representant, har overtatt tingen.</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Hvis ikke leveringstidspunkt fremgår av bestillingsløsningen, skal selgeren levere varen til
|
||||
kjøper uten unødig opphold og senest 30 dager etter bestillingen fra kunden. Varen skal leveres
|
||||
hos kjøperen med mindre annet er særskilt avtalt mellom partene.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">7. Risikoen for varen</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Risikoen for varen går over på kjøper når han, eller kjøpers representant, har fått varene levert
|
||||
i tråd med punkt 6.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">8. Angrerett</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Med mindre avtalen er unntatt fra angrerett, kan kjøperen angre kjøpet av varen i henhold til
|
||||
angrerettloven.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøperen må gi selger melding om bruk av angreretten innen 14 dager fra fristen begynner å løpe. I
|
||||
fristen inkluderes alle kalenderdager. Dersom fristen ender på en lørdag, helligdag eller
|
||||
høytidsdag forlenges fristen til nærmeste virkedag.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Angrefristen anses overholdt dersom melding er sendt før utløpet av fristen. Kjøper har
|
||||
bevisbyrden for at angreretten er blitt gjort gjeldende, og meldingen bør derfor skje skriftlig
|
||||
(angrerettskjema, e-post eller brev).
|
||||
</p>
|
||||
|
||||
<p class="mb-2">Angrefristen begynner å løpe:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Ved kjøp av enkeltstående varer vil angrefristen løpe fra dagen etter varen(e) er mottatt.
|
||||
</li>
|
||||
<li>
|
||||
Selges et abonnement, eller innebærer avtalen regelmessig levering av identiske varer, løper
|
||||
fristen fra dagen etter første forsendelse er mottatt.
|
||||
</li>
|
||||
<li>
|
||||
Består kjøpet av flere leveranser, vil angrefristen løpe fra dagen etter siste leveranse er
|
||||
mottatt.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p class="mb-2">
|
||||
Angrefristen utvides til 12 måneder etter utløpet av den opprinnelige fristen dersom selger ikke
|
||||
før avtaleinngåelsen opplyser om at det foreligger angrerett og standardisert angreskjema.
|
||||
Tilsvarende gjelder ved manglende opplysning om vilkår, tidsfrister og fremgangsmåte for å benytte
|
||||
angreretten. Sørger den næringsdrivende for å gi opplysningene i løpet av disse 12 månedene,
|
||||
utløper angrefristen likevel 14 dager etter den dagen kjøperen mottok opplysningene.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Ved bruk av angreretten må varen leveres tilbake til selgeren uten unødig opphold og senest 14
|
||||
dager fra melding om bruk av angreretten er gitt. Kjøper dekker de direkte kostnadene ved å
|
||||
returnere varen, med mindre annet er avtalt eller selger har unnlatt å opplyse om at kjøper skal
|
||||
dekke returkostnadene. Selgeren kan ikke fastsette gebyr for kjøperens bruk av angreretten.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøper kan prøve eller teste varen på en forsvarlig måte for å fastslå varens art, egenskaper og
|
||||
funksjon, uten at angreretten faller bort. Dersom prøving eller test av varen går utover hva som
|
||||
er forsvarlig og nødvendig, kan kjøperen bli ansvarlig for eventuell redusert verdi på varen.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Selgeren er forpliktet til å tilbakebetale kjøpesummen til kjøperen uten unødig opphold, og senest
|
||||
14 dager fra selgeren fikk melding om kjøperens beslutning om å benytte angreretten. Selger har
|
||||
rett til å holde tilbake betalingen til han/hun har mottatt varene fra kjøperen, eller til kjøper
|
||||
har lagt frem dokumentasjon for at varene er sendt tilbake.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">
|
||||
9. Forsinkelse og manglende levering - kjøpernes rettigheter og frist for å melde krav
|
||||
</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom selgeren ikke leverer varen eller leverer den for sent i henhold til avtalen mellom
|
||||
partene, og dette ikke skyldes kjøperen eller forhold på kjøperens side, kan kjøperen i henhold
|
||||
til reglene i forbrukerkjøpslovens kapittel 5 etter omstendighetene holde kjøpesummen tilbake,
|
||||
kreve oppfyllelse, heve avtalen og/eller kreve erstatning fra selgeren.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Ved krav om misligholdsbeføyelser bør meldingen av bevishensyn være skriftlig (for eksempel
|
||||
e-post).
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Oppfyllelse</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøper kan fastholde kjøpet og kreve oppfyllelse fra selger. Kjøper kan imidlertid ikke kreve
|
||||
oppfyllelse dersom det foreligger en hindring som selgeren ikke kan overvinne, eller dersom
|
||||
oppfyllelse vil medføre en så stor ulempe eller kostnad for selger at det står i vesentlig
|
||||
misforhold til kjøperens interesse i at selgeren oppfyller. Skulle vanskene falle bort innen
|
||||
rimelig tid, kan kjøper likevel kreve oppfyllelse.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøperen taper sin rett til å kreve oppfyllelse om han eller hun venter urimelig lenge med å
|
||||
fremme kravet.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Heving</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom selgeren ikke leverer varen på leveringstidspunktet, skal kjøperen oppfordre selger til å
|
||||
levere innen en rimelig tilleggsfrist for oppfyllelse. Dersom selger ikke leverer varen innen
|
||||
tilleggsfristen, kan kjøperen heve kjøpet.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøper kan imidlertid heve kjøpet umiddelbart hvis selger nekter å levere varen. Tilsvarende
|
||||
gjelder dersom levering til avtalt tid var avgjørende for inngåelsen av avtalen, eller dersom
|
||||
kjøperen har underrettet selger om at leveringstidspunktet er avgjørende.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Leveres tingen etter tilleggsfristen forbrukeren har satt eller etter leveringstidspunktet som var
|
||||
avgjørende for inngåelsen av avtalen, må krav om heving gjøres gjeldende innen rimelig tid etter
|
||||
at kjøperen fikk vite om leveringen.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Erstatning</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøperen kan kreve erstatning for lidt tap som følge av forsinkelsen. Dette gjelder imidlertid
|
||||
ikke dersom selgeren godtgjør at forsinkelsen skyldes hindring utenfor selgers kontroll som ikke
|
||||
med rimelighet kunne blitt tatt i betraktning på avtaletiden, unngått, eller overvunnet følgene
|
||||
av.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">
|
||||
10. Mangel ved varen - kjøperens rettigheter og reklamasjonsfrist
|
||||
</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Hvis det foreligger en mangel ved varen må kjøper innen rimelig tid etter at den ble oppdaget
|
||||
eller burde ha blitt oppdaget, gi selger melding om at han eller hun vil påberope seg mangelen.
|
||||
Kjøper har alltid reklamert tidsnok dersom det skjer innen 2 mnd. fra mangelen ble oppdaget eller
|
||||
burde blitt oppdaget. Reklamasjon kan skje senest to år etter at kjøper overtok varen. Dersom
|
||||
varen eller deler av den er ment å vare vesentlig lenger enn to år, er reklamasjonsfristen fem år.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom varen har en mangel og dette ikke skyldes kjøperen eller forhold på kjøperens side, kan
|
||||
kjøperen i henhold til reglene i forbrukerkjøpsloven kapittel 6 etter omstendighetene holde
|
||||
kjøpesummen tilbake, velge mellom retting og omlevering, kreve prisavslag, kreve avtalen hevet
|
||||
og/eller kreve erstatning fra selgeren.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">Reklamasjon til selgeren bør skje skriftlig.</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Retting eller omlevering</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøperen kan velge mellom å kreve mangelen rettet eller levering av tilsvarende ting. Selger kan
|
||||
likevel motsette seg kjøperens krav dersom gjennomføringen av kravet er umulig eller volder
|
||||
selgeren urimelige kostnader. Retting eller omlevering skal foretas innen rimelig tid. Selger har
|
||||
i utgangspunktet ikke rett til å foreta mer enn to avhjelpsforsøk for samme mangel.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Prisavslag</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Kjøper kan kreve et passende prisavslag dersom varen ikke blir rettet eller omlevert. Dette
|
||||
innebærer at forholdet mellom nedsatt og avtalt pris svarer til forholdet mellom tingens verdi i
|
||||
mangelfull og kontraktsmessig stand. Dersom særlige grunner taler for det, kan prisavslaget i
|
||||
stedet settes lik mangelens betydning for kjøperen.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Heving</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom varen ikke er rettet eller omlevert, kan kjøperen også heve kjøpet når mangelen ikke er
|
||||
uvesentlig.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">11. Selgerens rettigheter ved kjøperens mislighold</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom kjøperen ikke betaler eller oppfyller de øvrige pliktene etter avtalen eller loven, og
|
||||
dette ikke skyldes selgeren eller forhold på selgerens side, kan selgeren i henhold til reglene i
|
||||
forbrukerkjøpsloven kapittel 9 etter omstendighetene holde varen tilbake, kreve oppfyllelse av
|
||||
avtalen, kreve avtalen hevet samt kreve erstatning fra kjøperen. Selgeren vil også etter
|
||||
omstendighetene kunne kreve renter ved forsinket betaling, inkassogebyr og et rimelig gebyr ved
|
||||
uavhentede varer.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Oppfyllelse</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Selger kan fastholde kjøpet og kreve at kjøperen betaler kjøpesummen. Er varen ikke levert, taper
|
||||
selgeren sin rett dersom han venter urimelig lenge med å fremme kravet.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Heving</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Selger kan heve avtalen dersom det foreligger vesentlig betalingsmislighold eller annet vesentlig
|
||||
mislighold fra kjøperens side. Selger kan likevel ikke heve dersom hele kjøpesummen er betalt.
|
||||
Fastsetter selger en rimelig tilleggsfrist for oppfyllelse og kjøperen ikke betaler innen denne
|
||||
fristen, kan selger heve kjøpet.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Renter ved forsinket betaling/inkassogebyr</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom kjøperen ikke betaler kjøpesummen i henhold til avtalen, kan selger kreve renter av
|
||||
kjøpesummen etter forsinkelsesrenteloven. Ved manglende betaling kan kravet, etter forutgående
|
||||
varsel, bli sendt til inkasso. Kjøper kan da bli holdt ansvarlig for gebyr etter inkassoloven.
|
||||
</p>
|
||||
<h3 class="mt-1 text-lg font-semibold">Gebyr ved uavhentede ikke-forskuddsbetalte varer</h3>
|
||||
|
||||
<p class="mb-2">
|
||||
Dersom kjøperen unnlater å hente ubetalte varer, kan selger belaste kjøper med et gebyr. Gebyret
|
||||
skal maksimalt dekke selgerens faktiske utlegg for å levere varen til kjøperen. Et slikt gebyr kan
|
||||
ikke belastes kjøpere under 18 år.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">12. Garanti</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Garanti som gis av selgeren eller produsenten, gir kjøperen rettigheter i tillegg til de kjøperen
|
||||
allerede har etter ufravikelig lovgivning. En garanti innebærer dermed ingen begrensninger i
|
||||
kjøperens rett til reklamasjon og krav ved forsinkelse eller mangler etter punkt 9 og 10.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">13. Personopplysninger</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Behandlingsansvarlig for innsamlede personopplysninger er selger. Med mindre kjøperen samtykker
|
||||
til noe annet, kan selgeren, i tråd med personopplysningsloven, kun innhente og lagre de
|
||||
personopplysninger som er nødvendig for at selgeren skal kunne gjennomføre forpliktelsene etter
|
||||
avtalen. Kjøperens personopplysninger vil kun bli utlevert til andre hvis det er nødvendig for at
|
||||
selger skal få gjennomført avtalen med kjøperen, eller i lovbestemte tilfelle.
|
||||
</p>
|
||||
<h2 class="mb-1 text-xl font-semibold">14. Konfliktløsning</h2>
|
||||
|
||||
<p class="mb-2">
|
||||
Klager rettes til selger innen rimelig tid, jf. punkt 9 og 10. Partene skal forsøke å løse
|
||||
eventuelle tvister i minnelighet. Dersom dette ikke lykkes, kan kjøperen ta kontakt med
|
||||
Forbrukertilsynet for mekling. Forbrukertilsynet er tilgjengelig på telefon 23 400 600 eller
|
||||
<Link href="www.forbrukertilsynet.no">www.forbrukertilsynet.no</Link>.
|
||||
</p>
|
||||
|
||||
<p class="mb-2">
|
||||
Europa-Kommisjonens klageportal kan også brukes hvis du ønsker å inngi en klage. Det er særlig
|
||||
relevant, hvis du er forbruker bosatt i et annet EU-land. Klagen inngis her:
|
||||
<Link href="http://ec.europa.eu/odr">http://ec.europa.eu/odr</Link>.
|
||||
</p>
|
||||
@@ -1,23 +1,37 @@
|
||||
<script lang="ts">
|
||||
export let checked: boolean;
|
||||
|
||||
export let leftIcon: string | null = null;
|
||||
export let rightIcon: string | null = null;
|
||||
|
||||
export let invertColor = false;
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
{#if leftIcon}
|
||||
<img src={leftIcon} class={`h-6 ${invertColor && 'dark:invert'}`} alt="" />
|
||||
{/if}
|
||||
<label class="relative inline-flex cursor-pointer items-center">
|
||||
<input on:change {checked} type="checkbox" value="" class="peer sr-only" />
|
||||
<div
|
||||
class="h-6 w-11 rounded-full border-2 border-contrast-100 outline-2 transition-colors after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border-2 after:border-contrast-100 after:bg-contrast-900 after:transition-all after:content-[''] peer-checked:bg-accent peer-checked:after:translate-x-full dark:peer-focus:ring-accent"
|
||||
/>
|
||||
</label>
|
||||
{#if rightIcon}
|
||||
<img src={rightIcon} class={`h-6 ${invertColor && 'dark:invert'}`} alt="" />
|
||||
{/if}
|
||||
</div>
|
||||
<script lang="ts">
|
||||
export let checked: boolean;
|
||||
|
||||
export let leftIcon: string | null = null;
|
||||
export let rightIcon: string | null = null;
|
||||
|
||||
export let invertColor = false;
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
{#if leftIcon}
|
||||
<img
|
||||
src={leftIcon}
|
||||
class={`h-6 ${invertColor && "dark:invert"}`}
|
||||
alt=""
|
||||
/>
|
||||
{/if}
|
||||
<label class="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
on:change
|
||||
{checked}
|
||||
type="checkbox"
|
||||
value=""
|
||||
class="peer sr-only"
|
||||
/>
|
||||
<div
|
||||
class="h-6 w-11 rounded-full border-2 border-contrast-100 outline-2 transition-colors after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border-2 after:border-contrast-100 after:bg-contrast-900 after:transition-all after:content-[''] peer-checked:bg-accent peer-checked:after:translate-x-full dark:peer-focus:ring-accent"
|
||||
></div>
|
||||
</label>
|
||||
{#if rightIcon}
|
||||
<img
|
||||
src={rightIcon}
|
||||
class={`h-6 ${invertColor && "dark:invert"}`}
|
||||
alt=""
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
<script lang="ts" generics="T">
|
||||
import ListboxItem from './ListboxItem.svelte';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { setListboxContext, type ListboxItemAbstract } from './context';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
/** @description Determine if multiple items can be selected */
|
||||
export let allowMultiple = true;
|
||||
|
||||
/** @description Determine if multiple items can be selected */
|
||||
export let items: T[];
|
||||
|
||||
/** @description Determine if multiple items can be selected */
|
||||
export let defaultSelectedItems: T[];
|
||||
|
||||
/** @description Function called when the selected items changes */
|
||||
export let onChange: (items: T[]) => void;
|
||||
|
||||
let selectedItems: Writable<ListboxItemAbstract<T>[]> = writable([]);
|
||||
let registeredItems: Writable<ListboxItemAbstract<T>[]> = writable([]);
|
||||
let focusedItem: Writable<ListboxItemAbstract<T> | null> = writable(null);
|
||||
|
||||
function toggleItemSelected(item: ListboxItemAbstract<T>): void {
|
||||
if (item) {
|
||||
setFocus(item);
|
||||
if ($selectedItems.includes(item)) {
|
||||
if (allowMultiple) {
|
||||
$selectedItems = $selectedItems.filter((x) => x !== item);
|
||||
} else {
|
||||
$selectedItems = [];
|
||||
}
|
||||
} else {
|
||||
if (allowMultiple) {
|
||||
$selectedItems.push(item);
|
||||
$selectedItems = $selectedItems;
|
||||
} else {
|
||||
$selectedItems = [item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onChange($selectedItems.map((x) => x.item));
|
||||
}
|
||||
|
||||
function setFocus(item: ListboxItemAbstract<T>) {
|
||||
const itemEl = document.getElementById(item.id);
|
||||
$focusedItem = item;
|
||||
itemEl?.focus();
|
||||
}
|
||||
|
||||
function onKeyDown(e: KeyboardEvent, item: ListboxItemAbstract<T>) {
|
||||
const index = $registeredItems.findIndex((x) => x === item);
|
||||
if (index === -1) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
if ($registeredItems.length - 1 === index) {
|
||||
setFocus($registeredItems[0]);
|
||||
} else {
|
||||
setFocus($registeredItems[index + 1]);
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
if (index === 0) {
|
||||
setFocus($registeredItems[$registeredItems.length - 1]);
|
||||
} else {
|
||||
setFocus($registeredItems[index - 1]);
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
e.preventDefault();
|
||||
toggleItemSelected(item);
|
||||
break;
|
||||
case 'Home':
|
||||
setFocus($registeredItems[0]);
|
||||
break;
|
||||
case 'End':
|
||||
setFocus($registeredItems[$registeredItems.length - 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function registerItem(item: ListboxItemAbstract<T>): void {
|
||||
$registeredItems.push(item);
|
||||
$registeredItems = $registeredItems;
|
||||
if (defaultSelectedItems.includes(item.item)) {
|
||||
$selectedItems.push(item);
|
||||
$selectedItems = $selectedItems;
|
||||
}
|
||||
if ($registeredItems.length === 1) {
|
||||
$focusedItem = $registeredItems[0];
|
||||
}
|
||||
}
|
||||
|
||||
function unregisterItem(item: ListboxItemAbstract<T>): void {
|
||||
$registeredItems = $registeredItems.filter((x) => x.id !== item.id);
|
||||
$selectedItems = $selectedItems.filter((x) => x.id !== item.id);
|
||||
}
|
||||
|
||||
setListboxContext<T>({
|
||||
registerItem: registerItem,
|
||||
unregisterItem: unregisterItem,
|
||||
selectedItems: selectedItems,
|
||||
focusedItem: focusedItem
|
||||
});
|
||||
</script>
|
||||
|
||||
<ul
|
||||
class={twMerge('flex flex-col gap-2 p-4', $$restProps['class'])}
|
||||
role="listbox"
|
||||
aria-activedescendant={$focusedItem?.id}
|
||||
aria-multiselectable={allowMultiple}
|
||||
tabindex={0}
|
||||
>
|
||||
{#each items as item}
|
||||
<ListboxItem {onKeyDown} onClick={(e, x) => toggleItemSelected(x)} {item}>
|
||||
<slot {item} />
|
||||
</ListboxItem>
|
||||
{/each}
|
||||
</ul>
|
||||
@@ -1,51 +0,0 @@
|
||||
<script lang="ts" generics="T">
|
||||
import { onMount } from 'svelte';
|
||||
import { getListboxContext, type ListboxItemAbstract } from './context';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export let item: T;
|
||||
export let onClick: (e: MouseEvent, item: ListboxItemAbstract<T>) => void;
|
||||
export let onKeyDown: (e: KeyboardEvent, item: ListboxItemAbstract<T>) => void;
|
||||
|
||||
const id: string = `listbox-item-${
|
||||
crypto.randomUUID
|
||||
? crypto.randomUUID()
|
||||
: Math.floor(Date.now() * Math.random() * 100).toString()
|
||||
}`;
|
||||
|
||||
const itemAbstract: ListboxItemAbstract<T> = { id: id, item: item };
|
||||
|
||||
let {
|
||||
selectedItems: selectedItems,
|
||||
registerItem: register,
|
||||
unregisterItem: unregister,
|
||||
focusedItem: focusedItem
|
||||
} = getListboxContext<T>();
|
||||
|
||||
onMount(() => {
|
||||
register(itemAbstract);
|
||||
|
||||
return () => {
|
||||
unregister(itemAbstract);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<li
|
||||
{id}
|
||||
role="option"
|
||||
aria-selected={$selectedItems.includes(itemAbstract)}
|
||||
on:click={(e) => onClick(e, itemAbstract)}
|
||||
on:keydown={(e) => onKeyDown(e, itemAbstract)}
|
||||
tabindex={$focusedItem && $focusedItem === itemAbstract ? 0 : -1}
|
||||
class={twMerge(
|
||||
`cursor-pointer overflow-hidden rounded-md border-2 border-contrast-200 opacity-80 ${
|
||||
$selectedItems.includes(itemAbstract)
|
||||
? 'border-contrast-900 opacity-100 dark:border-accent'
|
||||
: ''
|
||||
}`,
|
||||
$$restProps['class']
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
</li>
|
||||
@@ -1,22 +0,0 @@
|
||||
import { getContext, setContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
type ListboxContext<T> = {
|
||||
registerItem: (item: ListboxItemAbstract<T>) => void;
|
||||
unregisterItem: (item: ListboxItemAbstract<T>) => void;
|
||||
selectedItems: Writable<ListboxItemAbstract<T>[]>;
|
||||
focusedItem: Writable<ListboxItemAbstract<T> | null>;
|
||||
};
|
||||
|
||||
export type ListboxItemAbstract<T> = {
|
||||
id: string;
|
||||
item: T;
|
||||
};
|
||||
|
||||
export function setListboxContext<T>(context: ListboxContext<T>): void {
|
||||
setContext('listbox', context);
|
||||
}
|
||||
|
||||
export function getListboxContext<T>(): ListboxContext<T> {
|
||||
return getContext('listbox') as ListboxContext<T>;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { getTabsContext } from './context';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export let label: string;
|
||||
|
||||
const id: string = `tab-${
|
||||
crypto.randomUUID
|
||||
? crypto.randomUUID()
|
||||
: Math.floor(Date.now() * Math.random() * 100).toString()
|
||||
}`;
|
||||
|
||||
const context = getTabsContext();
|
||||
const { selectedTab } = context;
|
||||
|
||||
onMount(() => {
|
||||
context.register({
|
||||
id: id,
|
||||
label: label
|
||||
});
|
||||
|
||||
return () => {
|
||||
context.unregister(id);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if $selectedTab === id}
|
||||
<div
|
||||
{...$$restProps}
|
||||
class={twMerge('rounded-md bg-primary-300 p-4 shadow-inner', $$restProps['class'])}
|
||||
role="tabpanel"
|
||||
aria-labelledby={id}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,102 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { setTabsContext, type TabAbstract } from './context';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
|
||||
export let buttonClass = '';
|
||||
|
||||
let tabs: TabAbstract[] = [];
|
||||
let selectedTab: Writable<string | null> = writable(null);
|
||||
|
||||
function setTab(id: string): void {
|
||||
const tab = tabs.find((x) => x.id == id);
|
||||
if (tab) {
|
||||
$selectedTab = tab.id;
|
||||
}
|
||||
}
|
||||
|
||||
function registerTab(tab: TabAbstract): void {
|
||||
tabs.push(tab);
|
||||
tabs = tabs;
|
||||
|
||||
if (tabs.length === 1) {
|
||||
$selectedTab = tabs[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
function unregisterTab(id: string): void {
|
||||
tabs = tabs.filter((x) => x.id !== id);
|
||||
}
|
||||
|
||||
function setFocus(tab: TabAbstract) {
|
||||
const tabEl = document.getElementById(tab.id);
|
||||
tabEl?.focus();
|
||||
}
|
||||
|
||||
function onKeyDown(e: KeyboardEvent, tab: TabAbstract) {
|
||||
const index = tabs.findIndex((x) => x.id === tab.id);
|
||||
if (index === -1) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
e.preventDefault();
|
||||
if (tabs.length - 1 === index) {
|
||||
setFocus(tabs[0]);
|
||||
} else {
|
||||
setFocus(tabs[index + 1]);
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
if (index === 0) {
|
||||
setFocus(tabs[tabs.length - 1]);
|
||||
} else {
|
||||
setFocus(tabs[index - 1]);
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
setFocus(tabs[0]);
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
setFocus(tabs[tabs.length - 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setTabsContext({
|
||||
register: registerTab,
|
||||
unregister: unregisterTab,
|
||||
selectedTab: selectedTab
|
||||
});
|
||||
</script>
|
||||
|
||||
<div {...$$restProps} class={twMerge('flex flex-col gap-4', $$restProps['class'])}>
|
||||
<div
|
||||
role="tablist"
|
||||
aria-labelledby="tablist"
|
||||
class="flex gap-4 overflow-auto rounded-md bg-primary-300 p-4 shadow-inner"
|
||||
>
|
||||
{#each tabs as tab, i}
|
||||
<button
|
||||
role="tab"
|
||||
type="button"
|
||||
id={tab.id}
|
||||
tabindex={$selectedTab === tab.id || ($selectedTab === null && i === 0) ? 0 : -1}
|
||||
aria-selected={$selectedTab === tab.id}
|
||||
aria-controls={tab.id}
|
||||
class={twMerge(
|
||||
`rounded-md border-2 border-contrast-200 bg-primary-100 px-4 py-2 hover:border-contrast-900 dark:hover:border-accent ${$selectedTab === tab.id ? 'border-contrast-900 dark:border-accent' : ''}`,
|
||||
buttonClass
|
||||
)}
|
||||
on:click={() => setTab(tab.id)}
|
||||
on:keydown={(e) => onKeyDown(e, tab)}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
@@ -1,21 +0,0 @@
|
||||
import { getContext, setContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
type TabsContext = {
|
||||
register: (tab: TabAbstract) => void;
|
||||
unregister: (id: string) => void;
|
||||
selectedTab: Writable<string | null>;
|
||||
};
|
||||
|
||||
export type TabAbstract = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export function setTabsContext(context: TabsContext): void {
|
||||
setContext('tabs', context);
|
||||
}
|
||||
|
||||
export function getTabsContext(): TabsContext {
|
||||
return getContext('tabs') as TabsContext;
|
||||
}
|
||||
Reference in New Issue
Block a user