Compare commits

..

10 Commits

Author SHA1 Message Date
916fefb10b entete 2024-05-29 22:50:00 +02:00
785ffac993 fix without nuxt 3.12 2024-05-16 19:45:10 +02:00
Michael Dausmann
460c859ab3 remove console.logs 2024-02-25 12:13:19 +11:00
Michael Dausmann
0abc4ec624 refactors 2024-02-25 00:31:55 +11:00
Michael Dausmann
786665e84e version 1.4.3 changelog 2024-02-19 02:57:35 +11:00
Michael Dausmann
d3391c21a6 update cookie consent and add contact page 2024-02-19 02:43:30 +11:00
Michael Dausmann
0219723a07 update daisyui 2024-02-19 01:54:49 +11:00
Michael Dausmann
e7d1f35777 update stripe and stripe api 2024-02-19 01:48:51 +11:00
Michael Dausmann
463cf7f194 update superjson and node types 2024-02-19 00:20:07 +11:00
Michael Dausmann
9daea204e5 more type cleanup 2024-02-17 14:54:45 +11:00
30 changed files with 1232 additions and 12169 deletions

View File

@@ -1,5 +1,5 @@
SUPABASE_URL=https://xxxxxxxxxxxxxxxxxxxx.supabase.co
SUPABASE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxx.xxxxxx-xxxxx
SUPABASE_URL=http://localhost:8843
SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_ENDPOINT_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ node_modules
.env
dist
junk
.DS_Store

View File

@@ -1,5 +1,18 @@
# Changelog
## Version 1.4.3
### Update All Dependencies to latest
- openai (3.3.0 -> 4.28.0)
- superjson (1.12.2 -> 2.2.1)
- node types (18.15.11 -> 20.11.19)
- stripe lib (11.12.0 -> 14.17.0)
- stripe api version (2022-11-15 -> 2023-10-16)
- cookie consent (2.9.2 -> 3.0.0)
- daisyui (2.51.5 -> 4.7.2)
- vitest (0.33.0 -> 1.3.0)
- other minor and patch versions
## Version 1.4.2
- Added Favicons and web manifest and referenced in nuxt.config (I used https://favicon.io/favicon-converter/ to generate the icon assets, seems to work well)
- Added patch folder to hold patch files, should make it easier to update repos based on earlier versions

10
app.vue
View File

@@ -1,12 +1,16 @@
<template>
<div class="flex flex-col min-h-screen bg-base-100">
<AppHeader />
<!-- <div class="flex flex-col min-h-screen bg-base-100"> -->
<div class="flex flex-col min-h-screen bg-gray-800">
<Entete />
<!-- <AppHeader /> -->
<div class="flex-grow">
<main>
<NuxtPage />
</main>
</div>
<AppFooter />
<!-- <AppFooter /> -->
<PiedPage />
</div>
</template>
<script setup lang="ts">

View File

@@ -4,6 +4,8 @@
<span class="px-2">|</span>
<NuxtLink to="/privacy">Privacy</NuxtLink>
<span class="px-2">|</span>
<button type="button" data-cc="c-settings">Cookie settings</button>
<NuxtLink to="/contact">Contact Us</NuxtLink>
<span class="px-2">|</span>
<button type="button" data-cc="show-preferencesModal">Cookie settings</button>
</div>
</template>

98
components/Entete.vue Normal file
View File

@@ -0,0 +1,98 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Dialog, DialogPanel } from '@headlessui/vue';
import { Bars3Icon, XMarkIcon } from '@heroicons/vue/24/outline';
const user = useSupabaseUser();
const navigation = [
{ name: 'Product', href: '#' },
{ name: 'Features', href: '#' },
{ name: 'Company', href: '#' }
];
const mobileMenuOpen = ref(false);
</script>
<template>
<header class="bg-primary">
<nav
class="mx-auto flex max-w-7xl items-center justify-between p-6 lg:px-8"
aria-label="Global">
<div class="flex flex-1">
<div class="hidden lg:flex lg:gap-x-12">
<a
v-for="item in navigation"
:key="item.name"
:href="item.href"
class="text-sm font-semibold leading-6 text-gray-200 hover:text-white"
>{{ item.name }}</a
>
</div>
<div class="flex lg:hidden">
<button
type="button"
class="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-200"
@click="mobileMenuOpen = true">
<span class="sr-only">Open main menu</span>
<Bars3Icon class="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
<a href="#" class="-m-1.5 p-1.5">
<span class="sr-only">Your Company</span>
<img
class="h-8 w-auto"
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
alt="" />
</a>
<div class="flex flex-1 justify-end">
<a href="#" class="text-sm font-semibold leading-6 text-gray-200"
>Log in <span aria-hidden="true">&rarr;</span></a
>
</div>
</nav>
<Dialog
class="lg:hidden"
@close="mobileMenuOpen = false"
:open="mobileMenuOpen">
<div class="fixed inset-0 z-10" />
<DialogPanel
class="fixed inset-y-0 left-0 z-10 w-full overflow-y-auto bg-white px-6 py-6">
<div class="flex items-center justify-between">
<div class="flex flex-1">
<button
type="button"
class="-m-2.5 rounded-md p-2.5 text-gray-700"
@click="mobileMenuOpen = false">
<span class="sr-only">Close menu</span>
<XMarkIcon class="h-6 w-6" aria-hidden="true" />
</button>
</div>
<a href="#" class="-m-1.5 p-1.5">
<span class="sr-only">Your Company</span>
<img
class="h-8 w-auto"
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
alt="" />
</a>
<div class="flex flex-1 justify-end">
<a href="#" class="text-sm font-semibold leading-6 text-gray-200"
>Log in <span aria-hidden="true">&rarr;</span></a
>
</div>
</div>
<div class="mt-6 space-y-2">
<a
v-for="item in navigation"
:key="item.name"
:href="item.href"
class="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
>{{ item.name }}</a
>
</div>
</DialogPanel>
</Dialog>
</header>
</template>

119
components/Entrepreneur.vue Normal file
View File

@@ -0,0 +1,119 @@
<template>
<div class="bg-gray-900 py-24 sm:py-32">
<div class="mx-auto max-w-7xl px-6 text-center lg:px-8">
<div class="mx-auto max-w-2xl">
<h2 class="text-3xl font-bold tracking-tight text-white sm:text-4xl">
Meet our team
</h2>
<p class="mt-4 text-lg leading-8 text-gray-400">
Were a dynamic group of individuals who are passionate about what we
do.
</p>
</div>
<ul
role="list"
class="mx-auto mt-20 grid max-w-2xl grid-cols-1 gap-6 sm:grid-cols-2 lg:mx-0 lg:max-w-none lg:grid-cols-3 lg:gap-8">
<li
v-for="person in people"
:key="person.name"
class="rounded-2xl bg-gray-800 px-8 py-10">
<img
class="mx-auto h-48 w-48 rounded-full md:h-56 md:w-56"
:src="person.imageUrl"
alt="" />
<h3
class="mt-6 text-base font-semibold leading-7 tracking-tight text-white">
{{ person.name }}
</h3>
<p class="text-sm leading-6 text-gray-400">{{ person.role }}</p>
<ul role="list" class="mt-6 flex justify-center gap-x-6">
<li>
<a :href="person.xUrl" class="text-gray-400 hover:text-gray-300">
<span class="sr-only">X</span>
<svg
class="h-5 w-5"
aria-hidden="true"
fill="currentColor"
viewBox="0 0 20 20">
<path
d="M11.4678 8.77491L17.2961 2H15.915L10.8543 7.88256L6.81232 2H2.15039L8.26263 10.8955L2.15039 18H3.53159L8.87581 11.7878L13.1444 18H17.8063L11.4675 8.77491H11.4678ZM9.57608 10.9738L8.95678 10.0881L4.02925 3.03974H6.15068L10.1273 8.72795L10.7466 9.61374L15.9156 17.0075H13.7942L9.57608 10.9742V10.9738Z" />
</svg>
</a>
</li>
<li>
<a
:href="person.linkedinUrl"
class="text-gray-400 hover:text-gray-300">
<span class="sr-only">LinkedIn</span>
<svg
class="h-5 w-5"
aria-hidden="true"
fill="currentColor"
viewBox="0 0 20 20">
<path
fill-rule="evenodd"
d="M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z"
clip-rule="evenodd" />
</svg>
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</template>
<script setup>
const people = [
{
name: 'Leonard Krasner',
role: 'Senior Designer',
imageUrl:
'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80',
xUrl: '#',
linkedinUrl: '#'
},
{
name: 'Leonard Krasner',
role: 'Senior Designer',
imageUrl:
'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80',
xUrl: '#',
linkedinUrl: '#'
},
{
name: 'Leonard Krasner',
role: 'Senior Designer',
imageUrl:
'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80',
xUrl: '#',
linkedinUrl: '#'
},
{
name: 'Leonard Krasner',
role: 'Senior Designer',
imageUrl:
'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80',
xUrl: '#',
linkedinUrl: '#'
},
{
name: 'Leonard Krasner',
role: 'Senior Designer',
imageUrl:
'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80',
xUrl: '#',
linkedinUrl: '#'
},
{
name: 'Leonard Krasner',
role: 'Senior Designer',
imageUrl:
'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80',
xUrl: '#',
linkedinUrl: '#'
}
// More people...
];
</script>

53
components/Highlight.vue Normal file
View File

@@ -0,0 +1,53 @@
<template>
<div class="bg-gray">
<main class="isolate">
<!-- Hero section -->
<div
class="relative isolate -z-10 overflow-hidden bg-gradient-to-b from-indigo-100/20 pt-14">
<!-- <div
class="absolute inset-y-0 right-1/2 -z-10 -mr-96 w-[200%] origin-top-right skew-x-[-30deg] bg-white shadow-xl shadow-indigo-600/10 ring-1 ring-indigo-50 sm:-mr-80 lg:-mr-96"
aria-hidden="true" /> -->
<div
class="absolute inset-y-0 right-1/2 -z-10 -mr-96 w-[200%] origin-top-right skew-x-[-30deg] bg-gray-900 shadow-xl shadow-indigo-600/10 ring-1 ring-indigo-50 sm:-mr-80 lg:-mr-96"
aria-hidden="true" />
<div class="mx-auto max-w-7xl px-6 py-32 sm:py-40 lg:px-8">
<div
class="mx-auto max-w-2xl lg:mx-0 lg:grid lg:max-w-none lg:grid-cols-2 lg:gap-x-16 lg:gap-y-6 xl:grid-cols-1 xl:grid-rows-1 xl:gap-x-8">
<h1
class="max-w-2xl text-4xl font-bold tracking-tight text-gray-100 sm:text-6xl lg:col-span-2 xl:col-auto">
Were a passionate group of people working from around the world
to build the future of ecommerce.
</h1>
<div class="mt-6 max-w-xl lg:mt-0 xl:col-end-1 xl:row-start-1">
<p class="text-lg leading-8 text-gray-400">
Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui
lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat
fugiat aliqua. Anim aute id magna aliqua ad ad non deserunt
sunt. Qui irure qui lorem cupidatat commodo.
</p>
</div>
<video
class="mt-10 aspect-[6/5] w-full max-w-lg rounded-2xl object-cover sm:mt-16 lg:mt-0 lg:max-w-none xl:row-span-2 xl:row-end-2 xl:mt-36"
controls>
<source
src="https://docs.material-tailwind.com/demo.mp4"
type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
</div>
<div
class="absolute inset-x-0 bottom-0 -z-10 h-24 bg-gradient-to-t from-grey-100 sm:h-32" />
</div>
</main>
</div>
</template>
<script setup>
import { defineComponent, h, ref } from 'vue';
import { Dialog, DialogPanel } from '@headlessui/vue';
import { Bars3Icon, XMarkIcon } from '@heroicons/vue/24/outline';
const mobileMenuOpen = ref(false);
</script>

226
components/PiedPage.vue Normal file
View File

@@ -0,0 +1,226 @@
<template>
<div class="bg-gray-900">
<!-- Footer -->
<footer class="mt-32 sm:mt-40" aria-labelledby="footer-heading">
<h2 id="footer-heading" class="sr-only">Footer</h2>
<div class="mx-auto max-w-7xl px-6 pb-8 lg:px-8">
<div
class="border-t border-gray-900/10 pt-20 xl:grid xl:grid-cols-3 xl:gap-8">
<div class="grid grid-cols-2 gap-8 xl:col-span-2">
<div class="md:grid md:grid-cols-2 md:gap-8">
<div>
<h3 class="text-sm font-semibold leading-6 text-white">
Solutions
</h3>
<ul role="list" class="mt-6 space-y-4">
<li
v-for="item in footerNavigation.solutions"
:key="item.name">
<a
:href="item.href"
class="text-sm leading-6 text-gray-400 hover:text-white"
>{{ item.name }}</a
>
</li>
</ul>
</div>
<div class="mt-10 md:mt-0">
<h3 class="text-sm font-semibold leading-6 text-white">
Support
</h3>
<ul role="list" class="mt-6 space-y-4">
<li v-for="item in footerNavigation.support" :key="item.name">
<a
:href="item.href"
class="text-sm leading-6 text-gray-400 hover:text-white"
>{{ item.name }}</a
>
</li>
</ul>
</div>
</div>
<div class="md:grid md:grid-cols-2 md:gap-8">
<div>
<h3 class="text-sm font-semibold leading-6 text-white">
Company
</h3>
<ul role="list" class="mt-6 space-y-4">
<li v-for="item in footerNavigation.company" :key="item.name">
<a
:href="item.href"
class="text-sm leading-6 text-gray-400 hover:text-white"
>{{ item.name }}</a
>
</li>
</ul>
</div>
<div class="mt-10 md:mt-0">
<h3 class="text-sm font-semibold leading-6 text-white">
Legal
</h3>
<ul role="list" class="mt-6 space-y-4">
<li v-for="item in footerNavigation.legal" :key="item.name">
<a
:href="item.href"
class="text-sm leading-6 text-gray-400 hover:text-white"
>{{ item.name }}</a
>
</li>
</ul>
</div>
</div>
</div>
<div class="mt-10 xl:mt-0">
<h3 class="text-sm font-semibold leading-6 text-gray-900">
Subscribe to our newsletter
</h3>
<p class="mt-2 text-sm leading-6 text-gray-600">
The latest news, articles, and resources, sent to your inbox
weekly.
</p>
<form class="mt-6 sm:flex sm:max-w-md">
<label for="email-address" class="sr-only">Email address</label>
<input
type="email"
name="email-address"
id="email-address"
autocomplete="email"
:required="inputrequired"
class="w-full min-w-0 appearance-none rounded-md border-0 bg-white px-3 py-1.5 text-base text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:w-64 sm:text-sm sm:leading-6 xl:w-full"
placeholder="Enter your email" />
<div class="mt-4 sm:ml-4 sm:mt-0 sm:flex-shrink-0">
<button
type="submit"
class="flex w-full items-center justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
Subscribe
</button>
</div>
</form>
</div>
</div>
<div
class="mt-16 border-t border-gray-900/10 pt-8 sm:mt-20 md:flex md:items-center md:justify-between lg:mt-24">
<div class="flex space-x-6 md:order-2">
<a
v-for="item in footerNavigation.social"
:key="item.name"
:href="item.href"
class="text-gray-200 hover:text-white">
<span class="sr-only">{{ item.name }}</span>
<component :is="item.icon" class="h-6 w-6" aria-hidden="true" />
</a>
</div>
<p class="mt-8 text-xs leading-5 text-gray-500 md:order-1 md:mt-0">
&copy; 2020 Your Company, Inc. All rights reserved.
</p>
</div>
</div>
</footer>
</div>
</template>
<script setup lang="ts">
import { defineComponent, h, ref } from 'vue';
import { Dialog, DialogPanel } from '@headlessui/vue';
import { Bars3Icon, XMarkIcon } from '@heroicons/vue/24/outline';
const inputrequired = ref(false);
const footerNavigation = {
solutions: [
{ name: 'Marketing', href: '#' },
{ name: 'Analytics', href: '#' },
{ name: 'Commerce', href: '#' },
{ name: 'Insights', href: '#' }
],
support: [
{ name: 'Pricing', href: '#' },
{ name: 'Documentation', href: '#' },
{ name: 'Guides', href: '#' },
{ name: 'API Status', href: '#' }
],
company: [
{ name: 'About', href: '#' },
{ name: 'Blog', href: '#' },
{ name: 'Jobs', href: '#' },
{ name: 'Press', href: '#' },
{ name: 'Partners', href: '#' }
],
legal: [
{ name: 'Claim', href: '#' },
{ name: 'Privacy', href: '#' },
{ name: 'Terms', href: '#' }
],
social: [
{
name: 'Facebook',
href: '#',
icon: defineComponent({
render: () =>
h('svg', { fill: 'currentColor', viewBox: '0 0 24 24' }, [
h('path', {
'fill-rule': 'evenodd',
d: 'M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z',
'clip-rule': 'evenodd'
})
])
})
},
{
name: 'Instagram',
href: '#',
icon: defineComponent({
render: () =>
h('svg', { fill: 'currentColor', viewBox: '0 0 24 24' }, [
h('path', {
'fill-rule': 'evenodd',
d: 'M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z',
'clip-rule': 'evenodd'
})
])
})
},
{
name: 'X',
href: '#',
icon: defineComponent({
render: () =>
h('svg', { fill: 'currentColor', viewBox: '0 0 24 24' }, [
h('path', {
d: 'M13.6823 10.6218L20.2391 3H18.6854L12.9921 9.61788L8.44486 3H3.2002L10.0765 13.0074L3.2002 21H4.75404L10.7663 14.0113L15.5685 21H20.8131L13.6819 10.6218H13.6823ZM11.5541 13.0956L10.8574 12.0991L5.31391 4.16971H7.70053L12.1742 10.5689L12.8709 11.5655L18.6861 19.8835H16.2995L11.5541 13.096V13.0956Z'
})
])
})
},
{
name: 'GitHub',
href: '#',
icon: defineComponent({
render: () =>
h('svg', { fill: 'currentColor', viewBox: '0 0 24 24' }, [
h('path', {
'fill-rule': 'evenodd',
d: 'M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z',
'clip-rule': 'evenodd'
})
])
})
},
{
name: 'YouTube',
href: '#',
icon: defineComponent({
render: () =>
h('svg', { fill: 'currentColor', viewBox: '0 0 24 24' }, [
h('path', {
'fill-rule': 'evenodd',
d: 'M19.812 5.418c.861.23 1.538.907 1.768 1.768C21.998 8.746 22 12 22 12s0 3.255-.418 4.814a2.504 2.504 0 0 1-1.768 1.768c-1.56.419-7.814.419-7.814.419s-6.255 0-7.814-.419a2.505 2.505 0 0 1-1.768-1.768C2 15.255 2 12 2 12s0-3.255.417-4.814a2.507 2.507 0 0 1 1.768-1.768C5.744 5 11.998 5 11.998 5s6.255 0 7.814.418ZM15.194 12 10 15V9l5.194 3Z',
'clip-rule': 'evenodd'
})
])
})
}
]
};
const mobileMenuOpen = ref(false);
</script>

View File

@@ -101,13 +101,13 @@ export namespace AccountService {
account_id: number,
membership_id: number
): Promise<MembershipWithAccount> {
const membership = prisma_client.membership.findFirstOrThrow({
const membership = await prisma_client.membership.findFirstOrThrow({
where: {
id: membership_id
}
});
if ((await membership).account_id != account_id) {
if (membership.account_id != account_id) {
throw new Error(`Membership does not belong to current account`);
}
@@ -126,13 +126,13 @@ export namespace AccountService {
account_id: number,
membership_id: number
): Promise<MembershipWithAccount> {
const membership = prisma_client.membership.findFirstOrThrow({
const membership = await prisma_client.membership.findFirstOrThrow({
where: {
id: membership_id
}
});
if ((await membership).account_id != account_id) {
if (membership.account_id != account_id) {
throw new Error(`Membership does not belong to current account`);
}
@@ -214,7 +214,7 @@ export namespace AccountService {
length: 10,
numbers: true
});
return prisma_client.account.update({
return await prisma_client.account.update({
where: { id: account_id },
data: { join_password }
});

View File

@@ -4,10 +4,6 @@ import { AccountLimitError } from './errors';
import { AccountService } from './account.service';
export namespace NotesService {
export async function getAllNotes() {
return prisma_client.note.findMany();
}
export async function getNoteById(id: number) {
return prisma_client.note.findUniqueOrThrow({ where: { id } });
}

12132
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "supanuxt-saas",
"version": "1.4.2",
"version": "1.4.3",
"author": {
"name": "Michael Dausmann",
"email": "mdausmann@gmail.com",
@@ -9,7 +9,7 @@
"license": "MIT",
"private": true,
"prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
"seed": "pnpx tsx prisma/seed.ts"
},
"scripts": {
"build": "nuxt build",
@@ -20,31 +20,33 @@
"test": "vitest"
},
"devDependencies": {
"@heroicons/vue": "^2.1.3",
"@nuxt/test-utils": "^3.11.0",
"@nuxtjs/supabase": "^1.1.6",
"@nuxtjs/tailwindcss": "^6.11.4",
"@prisma/client": "^5.9.1",
"@prisma/client": "^5.13.0",
"@tailwindcss/typography": "^0.5.10",
"@types/node": "^18.19.17",
"@types/node": "^20.12.7",
"nuxt": "^3.10.2",
"nuxt-headlessui": "^1.2.0",
"nuxt-icon": "^0.6.8",
"prisma": "^5.9.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"typescript": "^5.4.5",
"vitest": "^1.3.0"
},
"dependencies": {
"@pinia/nuxt": "^0.5.1",
"@trpc/client": "^10.45.1",
"@trpc/server": "^10.45.1",
"daisyui": "^2.52.0",
"daisyui": "^4.7.2",
"generate-password-ts": "^1.6.5",
"openai": "^4.28.0",
"pinia": "^2.1.7",
"stripe": "^11.18.0",
"superjson": "^1.13.3",
"stripe": "^14.17.0",
"superjson": "^2.2.1",
"trpc-nuxt": "^0.10.19",
"vanilla-cookieconsent": "^2.9.2",
"vanilla-cookieconsent": "^3.0.0",
"zod": "^3.22.4"
},
"overrides": {

10
pages/contact.vue Normal file
View File

@@ -0,0 +1,10 @@
<template>
<div class="prose lg:prose-xl m-5">
<h1>Contact Us</h1>
<p>
Contact SupaNuxt SaaS on <a href="https://github.com/JavascriptMick/supanuxt-saas">github</a>
</p>
</div>
</template>

View File

@@ -6,233 +6,13 @@
}
});
</script>
<template>
<div class="container mx-auto m-5">
<!-- Hero section -->
<section class="bg-gray-100 py-20">
<div class="container mx-auto">
<div class="grid grid-cols-1 md:grid-cols-2 gap-16">
<div class="m-5">
<h1 class="text-5xl font-bold mb-4">Build Your Next SaaS Faster</h1>
<p class="text-gray-700 text-lg mb-8">
With SupaNuxt SaaS, you can easily get started building your next
web application. Our pre-configured tech stack and industry
leading features make it easy to get up and running in no time.
Look! this guy is working so fast, his hands are just a blur.. you
could be this fast.
</p>
<NuxtLink
to="/signup"
class="inline-block py-3 px-6 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>Get Started</NuxtLink
>
</div>
<div>
<img
src="~/assets/images/supanuxt_logo_200.png"
alt="SupaNuxt SaaS Logo" />
</div>
</div>
</div>
</section>
<!-- Hightlight Section -->
<Highlight />
<section class="py-12">
<div class="container px-4 mx-auto">
<div
class="flex flex-col md:flex-row items-center justify-center md:justify-between mb-8">
<h2 class="text-3xl font-bold mb-4 md:mb-0">Tech Stack</h2>
</div>
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-full">
<ul class="grid grid-cols-3 gap-10 list-none">
<li>
<Icon name="skill-icons:nuxtjs-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Nuxt 3</h3>
<p class="mt-2 text-base text-gray-500">
The Progressive Vue.js Framework
</p>
</li>
<li>
<Icon name="skill-icons:supabase-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Supabase</h3>
<p class="mt-2 text-base text-gray-500">
Auth including OAuth + Postgresql instance
</p>
</li>
<li>
<Icon
name="skill-icons:postgresql-dark"
class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">PostgreSQL</h3>
<p class="mt-2 text-base text-gray-500">Relational Database</p>
</li>
<li>
<Icon name="logos:prisma" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Prisma</h3>
<p class="mt-2 text-base text-gray-500">
Schema management + Strongly typed client
</p>
</li>
<li>
<Icon name="simple-icons:trpc" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">TRPC</h3>
<p class="mt-2 text-base text-gray-500">
Server/Client communication with Strong types, SSR compatible
</p>
</li>
<li>
<Icon name="skill-icons:vuejs-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Pinia</h3>
<p class="mt-2 text-base text-gray-500">State Store</p>
</li>
<li>
<Icon name="logos:stripe" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Stripe</h3>
<p class="mt-2 text-base text-gray-500">
Payments including Webhook integration
</p>
</li>
<li>
<Icon
name="skill-icons:tailwindcss-dark"
class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Tailwind</h3>
<p class="mt-2 text-base text-gray-500">
A utility-first CSS framework
</p>
</li>
<li>
<Icon name="skill-icons:vuejs-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Vue.js</h3>
<p class="mt-2 text-base text-gray-500">
The Progressive JavaScript Framework
</p>
</li>
<li>
<Icon name="logos:openai-icon" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">OpenAI</h3>
<p class="mt-2 text-base text-gray-500">
AI Completions including Note generation from prompt
</p>
</li>
</ul>
</div>
</div>
<div
class="flex flex-col md:flex-row items-center justify-center md:justify-between mb-8">
<h2 class="text-3xl font-bold mb-4 md:mb-0">Features</h2>
</div>
<!-- User Management (text left) -->
<div class="flex flex-col md:flex-row-reverse items-center mb-16">
<div class="md:w-1/2 md:ml-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_user_management.jpeg"
alt="User Management"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:ml-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">User Management</h3>
<p class="mb-4">
SupaNuxt SaaS includes robust user management features, including
authentication with social login (oauth) or email/password,
management of user roles and permissions, and multi-user/team
accounts that permit multiple users to share plan features
including a team administration facility and user roles within
team. This is a great feature for businesses or community groups
who want to share the cost of the plan.
</p>
</div>
</div>
<!-- DB Schema (text right)-->
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-1/2 md:mr-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_db_schema_management.jpeg"
alt="DB Schema Management"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:mr-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">DB Schema Management</h3>
<p class="mb-4">
We use Prisma for schema management to make sure you can easily
manage and keep track of your database schema. We also utilise
Prisma based strong types which, with some help from TRPC,
penetrate the entire stack all the way to the web front end. This
ensures that you can move fast with your feature development,
alter schema and have those type changes instantly available and
validated everywhere.
</p>
</div>
</div>
<!-- Config (text left) -->
<div class="flex flex-col md:flex-row-reverse items-center mb-16">
<div class="md:w-1/2 md:ml-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_config_environment.jpeg"
alt="Config and Environment"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:ml-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">Config and Environment</h3>
<p class="mb-4">
SupaNuxt SaaS includes an approach to config and environment
management that enables customisation and management of api keys.
</p>
</div>
</div>
<!-- State Management (text right)-->
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-1/2 md:mr-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_state_management.jpeg"
alt="State Management"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:mr-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">State Management</h3>
<p class="mb-4">
SupaNuxt SaaS includes multi modal state management that supports
both Single Page Application (SPA) pages such as dashboards and
Server Side Rendered (SSR) style pages for public content that are
crawlable by Search engines like google and facilitate excellent
Search Engine Optimisation (SEO).
</p>
</div>
</div>
<!-- Stripe (text left) -->
<div class="flex flex-col md:flex-row-reverse items-center mb-16">
<div class="md:w-1/2 md:ml-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_stripe_integration.jpeg"
alt="Stripe Integration"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:ml-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">Stripe Integration</h3>
<p class="mb-4">
SupaNuxt SaaS includes Stripe integration for subscription
payments including Subscription based support for multi pricing
and multiple plans.
</p>
</div>
</div>
<!-- Tailwind (text right)-->
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-1/2 md:mr-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_style_system.jpeg"
alt="Style System"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:mr-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">Style System</h3>
<p class="mb-4">
SupaNuxt SaaS includes Tailwind integration for site styling
including a themable UI components with daisyUI
</p>
</div>
</div>
</div>
</section>
<!-- Entrepreneur Section -->
<Entrepreneur />
</div>
</template>

238
pages/indexsave.vue Normal file
View File

@@ -0,0 +1,238 @@
<script setup lang="ts">
const user = useSupabaseUser();
watchEffect(() => {
if (user.value) {
navigateTo('/dashboard', { replace: true });
}
});
</script>
<template>
<div class="container mx-auto m-5">
<!-- Hero section -->
<section class="bg-gray-100 py-20">
<div class="container mx-auto">
<div class="grid grid-cols-1 md:grid-cols-2 gap-16">
<div class="m-5">
<h1 class="text-5xl font-bold mb-4">Build Your Next SaaS Faster</h1>
<p class="text-gray-700 text-lg mb-8">
With SupaNuxt SaaS, you can easily get started building your next
web application. Our pre-configured tech stack and industry
leading features make it easy to get up and running in no time.
Look! this guy is working so fast, his hands are just a blur.. you
could be this fast.
</p>
<NuxtLink
to="/signup"
class="inline-block py-3 px-6 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>Get Started</NuxtLink
>
</div>
<div>
<img
src="~/assets/images/supanuxt_logo_200.png"
alt="SupaNuxt SaaS Logo" />
</div>
</div>
</div>
</section>
<section class="py-12">
<div class="container px-4 mx-auto">
<div
class="flex flex-col md:flex-row items-center justify-center md:justify-between mb-8">
<h2 class="text-3xl font-bold mb-4 md:mb-0">Tech Stack</h2>
</div>
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-full">
<ul class="grid grid-cols-3 gap-10 list-none">
<li>
<Icon name="skill-icons:nuxtjs-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Nuxt 3</h3>
<p class="mt-2 text-base text-gray-500">
The Progressive Vue.js Framework
</p>
</li>
<li>
<Icon name="skill-icons:supabase-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Supabase</h3>
<p class="mt-2 text-base text-gray-500">
Auth including OAuth + Postgresql instance
</p>
</li>
<li>
<Icon
name="skill-icons:postgresql-dark"
class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">PostgreSQL</h3>
<p class="mt-2 text-base text-gray-500">Relational Database</p>
</li>
<li>
<Icon name="logos:prisma" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Prisma</h3>
<p class="mt-2 text-base text-gray-500">
Schema management + Strongly typed client
</p>
</li>
<li>
<Icon name="simple-icons:trpc" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">TRPC</h3>
<p class="mt-2 text-base text-gray-500">
Server/Client communication with Strong types, SSR compatible
</p>
</li>
<li>
<Icon name="skill-icons:vuejs-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Pinia</h3>
<p class="mt-2 text-base text-gray-500">State Store</p>
</li>
<li>
<Icon name="logos:stripe" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Stripe</h3>
<p class="mt-2 text-base text-gray-500">
Payments including Webhook integration
</p>
</li>
<li>
<Icon
name="skill-icons:tailwindcss-dark"
class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Tailwind</h3>
<p class="mt-2 text-base text-gray-500">
A utility-first CSS framework
</p>
</li>
<li>
<Icon name="skill-icons:vuejs-dark" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">Vue.js</h3>
<p class="mt-2 text-base text-gray-500">
The Progressive JavaScript Framework
</p>
</li>
<li>
<Icon name="logos:openai-icon" class="h-12 w-12 mb-2" />
<h3 class="text-xl font-medium text-gray-900">OpenAI</h3>
<p class="mt-2 text-base text-gray-500">
AI Completions including Note generation from prompt
</p>
</li>
</ul>
</div>
</div>
<div
class="flex flex-col md:flex-row items-center justify-center md:justify-between mb-8">
<h2 class="text-3xl font-bold mb-4 md:mb-0">Features</h2>
</div>
<!-- User Management (text left) -->
<div class="flex flex-col md:flex-row-reverse items-center mb-16">
<div class="md:w-1/2 md:ml-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_user_management.jpeg"
alt="User Management"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:ml-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">User Management</h3>
<p class="mb-4">
SupaNuxt SaaS includes robust user management features, including
authentication with social login (oauth) or email/password,
management of user roles and permissions, and multi-user/team
accounts that permit multiple users to share plan features
including a team administration facility and user roles within
team. This is a great feature for businesses or community groups
who want to share the cost of the plan.
</p>
</div>
</div>
<!-- DB Schema (text right)-->
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-1/2 md:mr-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_db_schema_management.jpeg"
alt="DB Schema Management"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:mr-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">DB Schema Management</h3>
<p class="mb-4">
We use Prisma for schema management to make sure you can easily
manage and keep track of your database schema. We also utilise
Prisma based strong types which, with some help from TRPC,
penetrate the entire stack all the way to the web front end. This
ensures that you can move fast with your feature development,
alter schema and have those type changes instantly available and
validated everywhere.
</p>
</div>
</div>
<!-- Config (text left) -->
<div class="flex flex-col md:flex-row-reverse items-center mb-16">
<div class="md:w-1/2 md:ml-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_config_environment.jpeg"
alt="Config and Environment"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:ml-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">Config and Environment</h3>
<p class="mb-4">
SupaNuxt SaaS includes an approach to config and environment
management that enables customisation and management of api keys.
</p>
</div>
</div>
<!-- State Management (text right)-->
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-1/2 md:mr-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_state_management.jpeg"
alt="State Management"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:mr-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">State Management</h3>
<p class="mb-4">
SupaNuxt SaaS includes multi modal state management that supports
both Single Page Application (SPA) pages such as dashboards and
Server Side Rendered (SSR) style pages for public content that are
crawlable by Search engines like google and facilitate excellent
Search Engine Optimisation (SEO).
</p>
</div>
</div>
<!-- Stripe (text left) -->
<div class="flex flex-col md:flex-row-reverse items-center mb-16">
<div class="md:w-1/2 md:ml-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_stripe_integration.jpeg"
alt="Stripe Integration"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:ml-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">Stripe Integration</h3>
<p class="mb-4">
SupaNuxt SaaS includes Stripe integration for subscription
payments including Subscription based support for multi pricing
and multiple plans.
</p>
</div>
</div>
<!-- Tailwind (text right)-->
<div class="flex flex-col md:flex-row items-center mb-16">
<div class="md:w-1/2 md:mr-8 mb-8 md:mb-0">
<img
src="~/assets/images/landing_style_system.jpeg"
alt="Style System"
class="w-full rounded-lg shadow-lg mb-4 md:mb-0 md:mr-4" />
</div>
<div class="md:w-1/2">
<h3 class="text-xl font-bold mb-4">Style System</h3>
<p class="mb-4">
SupaNuxt SaaS includes Tailwind integration for site styling
including a themable UI components with daisyUI
</p>
</div>
</div>
</div>
</section>
</div>
</template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { AccountWithMembers } from '~~/lib/services/service.types';
import { type AccountWithMembers } from '~~/lib/services/service.types';
const route = useRoute();
const { join_password }: { join_password?: string } = route.params;

View File

@@ -2,7 +2,7 @@
import Stripe from 'stripe';
const config = useRuntimeConfig();
const stripe = new Stripe(config.stripeSecretKey, {
apiVersion: '2022-11-15'
apiVersion: '2023-10-16'
});
const route = useRoute();
let customer: Stripe.Response<Stripe.Customer | Stripe.DeletedCustomer>;

View File

@@ -1,129 +1,63 @@
import 'vanilla-cookieconsent/dist/cookieconsent.css';
import 'vanilla-cookieconsent/src/cookieconsent.js';
import * as CookieConsent from 'vanilla-cookieconsent';
export default defineNuxtPlugin(nuxtApp => {
// @ts-ignore
const cookieConsent = window.initCookieConsent();
/**
* All config. options available here:
* https://cookieconsent.orestbida.com/reference/configuration-reference.html
*/
CookieConsent.run({
categories: {
necessary: {
enabled: true, // this category is enabled by default
readOnly: true // this category cannot be disabled
},
analytics: {}
},
cookieConsent.run({
current_lang: 'en',
autoclear_cookies: true, // default: false
page_scripts: true, // default: false
// mode: 'opt-in' // default: 'opt-in'; value: 'opt-in' or 'opt-out'
// delay: 0, // default: 0
// auto_language: '', // default: null; could also be 'browser' or 'document'
// autorun: true, // default: true
// force_consent: false, // default: false
// hide_from_bots: true, // default: true
// remove_cookie_tables: false // default: false
// cookie_name: 'cc_cookie', // default: 'cc_cookie'
// cookie_expiration: 182, // default: 182 (days)
// cookie_necessary_only_expiration: 182 // default: disabled
// cookie_domain: location.hostname, // default: current domain
// cookie_path: '/', // default: root
// cookie_same_site: 'Lax', // default: 'Lax'
// use_rfc_cookie: false, // default: false
// revision: 0, // default: 0
// onFirstAction: function(user_preferences, cookie){
// // callback triggered only once on the first accept/reject action
// },
// onAccept: function (cookie) {
// // callback triggered on the first accept/reject action, and after each page load
// },
// onChange: function (cookie, changed_categories) {
// // callback triggered when user changes preferences after consent has already been given
// },
languages: {
en: {
consent_modal: {
title: 'We use cookies!',
description:
'Hi, this website uses essential cookies to ensure its proper operation and tracking cookies to understand how you interact with it. The latter will be set only after consent. <button type="button" data-cc="c-settings" class="cc-link">Let me choose</button>',
primary_btn: {
text: 'Accept all',
role: 'accept_all' // 'accept_selected' or 'accept_all'
language: {
default: 'en',
translations: {
en: {
consentModal: {
title: 'We use cookies',
description: 'Cookie modal description',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Reject all',
showPreferencesBtn: 'Manage Individual preferences'
},
secondary_btn: {
text: 'Reject all',
role: 'accept_necessary' // 'settings' or 'accept_necessary'
}
},
settings_modal: {
title: 'Cookie preferences',
save_settings_btn: 'Save settings',
accept_all_btn: 'Accept all',
reject_all_btn: 'Reject all',
close_btn_label: 'Close',
// cookie_table_caption: 'Cookie list',
cookie_table_headers: [
{ col1: 'Name' },
{ col2: 'Domain' },
{ col3: 'Expiration' },
{ col4: 'Description' }
],
blocks: [
{
title: 'Cookie usage 📢',
description:
'I use cookies to ensure the basic functionalities of the website and to enhance your online experience. You can choose for each category to opt-in/out whenever you want. For more details relative to cookies and other sensitive data, please read the full <a href="#" class="cc-link">privacy policy</a>.'
},
{
title: 'Strictly necessary cookies',
description:
'These cookies are essential for the proper functioning of my website. Without these cookies, the website would not work properly',
toggle: {
value: 'necessary',
enabled: true,
readonly: true // cookie categories with readonly=true are all treated as "necessary cookies"
}
},
{
title: 'Performance and Analytics cookies',
description:
'These cookies allow the website to remember the choices you have made in the past',
toggle: {
value: 'analytics', // your cookie category
enabled: false,
readonly: false
preferencesModal: {
title: 'Manage cookie preferences',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Reject all',
savePreferencesBtn: 'Accept current selection',
closeIconLabel: 'Close modal',
sections: [
{
title: 'Somebody said ... cookies?',
description: 'I want one!'
},
cookie_table: [
// list of all expected cookies
{
col1: '^_ga', // match all cookies starting with "_ga"
col2: 'google.com',
col3: '2 years',
col4: 'description ...',
is_regex: true
},
{
col1: '_gid',
col2: 'google.com',
col3: '1 day',
col4: 'description ...'
}
]
},
{
title: 'Advertisement and Targeting cookies',
description:
'These cookies collect information about how you use the website, which pages you visited and which links you clicked on. All of the data is anonymized and cannot be used to identify you',
toggle: {
value: 'targeting',
enabled: false,
readonly: false
{
title: 'Strictly Necessary cookies',
description:
'These cookies are essential for the proper functioning of the website and cannot be disabled.',
//this field will generate a toggle linked to the 'necessary' category
linkedCategory: 'necessary'
},
{
title: 'Performance and Analytics',
description:
'These cookies collect information about how you use our website. All of the data is anonymized and cannot be used to identify you.',
linkedCategory: 'analytics'
},
{
title: 'More information',
description:
'For any queries in relation to my policy on cookies and your choices, please <a href="/contact">contact us</a>'
}
},
{
title: 'More information',
description:
'For any queries in relation to our policy on cookies and your choices, please <a class="cc-link" href="#yourcontactpage">contact us</a>.'
}
]
]
}
}
}
}

View File

@@ -1,5 +1,6 @@
import { PrismaClient } from '@prisma/client';
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
async function main() {
const freeTrial = await prisma.plan.upsert({
where: { name: 'Free Trial' },

View File

@@ -1,4 +1,9 @@
import { EventHandler, EventHandlerRequest, H3Event, eventHandler } from 'h3';
import {
type EventHandler,
type EventHandlerRequest,
H3Event,
eventHandler
} from 'h3';
export const defineProtectedEventHandler = <T extends EventHandlerRequest>(
handler: EventHandler<T>

View File

@@ -4,7 +4,7 @@ import { AccountService } from '~~/lib/services/account.service';
import type { AccountWithMembers } from '~~/lib/services/service.types';
const config = useRuntimeConfig();
const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2022-11-15' });
const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2023-10-16' });
export default defineEventHandler(async event => {
const body = await readBody(event);

View File

@@ -2,7 +2,7 @@ import Stripe from 'stripe';
import { AccountService } from '~~/lib/services/account.service';
const config = useRuntimeConfig();
const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2022-11-15' });
const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2023-10-16' });
export default defineEventHandler(async event => {
const stripeSignature = getRequestHeader(event, 'stripe-signature');

View File

@@ -1,4 +1,4 @@
import { inferAsyncReturnType } from '@trpc/server';
import type { inferAsyncReturnType } from '@trpc/server';
import { H3Event } from 'h3';
export async function createContext(event: H3Event) {

View File

@@ -1,4 +1,5 @@
import { TRPCError } from '@trpc/server';
import { setCookie } from 'h3';
import {
router,
adminProcedure,

View File

@@ -8,7 +8,7 @@
* @see https://trpc.io/docs/v10/procedures
*/
import { initTRPC, TRPCError } from '@trpc/server';
import { Context } from './context';
import type { Context } from './context';
import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum';
import superjson from 'superjson';
import { AccountLimitError } from '~~/lib/services/errors';

View File

@@ -1,7 +1,10 @@
import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum';
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import { FullDBUser, MembershipWithUser } from '~~/lib/services/service.types';
import type {
FullDBUser,
MembershipWithUser
} from '~~/lib/services/service.types';
/*
This store manages User and Account state including the ActiveAccount

View File

@@ -1,6 +1,6 @@
import { Note } from '.prisma/client';
import type { Note } from '.prisma/client';
import { defineStore, storeToRefs } from 'pinia';
import { Ref } from 'vue';
import type { Ref } from 'vue';
export const useNotesStore = defineStore('notes', () => {
const accountStore = useAccountStore();

View File

@@ -1,13 +1,21 @@
export default {
plugins: [require("@tailwindcss/typography"), require("daisyui")],
theme: {
extend: {
colors: {
primary: '#1a202c',
secondary: '#2d3748'
}
}
},
plugins: [require('@tailwindcss/typography'), require('daisyui')],
daisyui: {
styled: true,
themes: ["acid", "night"],
themes: ['acid', 'night'],
base: true,
utils: true,
logs: true,
rtl: false,
prefix: "",
darkTheme: "night",
},
}
prefix: '',
darkTheme: 'night'
}
};

View File

@@ -1,4 +1,7 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"types": ["node"]
}
}