MVP #6.1 #82
|
@ -1,13 +1,13 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es2021: true,
|
es2021: true
|
||||||
},
|
},
|
||||||
extends: 'standard-with-typescript',
|
extends: 'standard-with-typescript',
|
||||||
overrides: [],
|
overrides: [],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
sourceType: 'module',
|
sourceType: 'module'
|
||||||
},
|
},
|
||||||
rules: {},
|
rules: {}
|
||||||
}
|
}
|
||||||
|
|
72
index.html
72
index.html
|
@ -1,66 +1,80 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"
|
||||||
|
/>
|
||||||
<title>Erant</title>
|
<title>Erant</title>
|
||||||
|
|
||||||
<!-- Serviceworker registration -->
|
<!-- Serviceworker registration -->
|
||||||
<script>
|
<script>
|
||||||
if (typeof navigator.serviceWorker !== 'undefined') {
|
if (typeof navigator.serviceWorker !== 'undefined') {
|
||||||
navigator.serviceWorker.register('sw.js')
|
navigator.serviceWorker.register('servicewworker.js')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Meta Pixel Code -->
|
<!-- Meta Pixel Code -->
|
||||||
<script>
|
<script>
|
||||||
!function(f,b,e,v,n,t,s)
|
!(function (f, b, e, v, n, t, s) {
|
||||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
if (f.fbq) return
|
||||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
n = f.fbq = function () {
|
||||||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments)
|
||||||
n.queue=[];t=b.createElement(e);t.async=!0;
|
}
|
||||||
t.src=v;s=b.getElementsByTagName(e)[0];
|
if (!f._fbq) f._fbq = n
|
||||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
n.push = n
|
||||||
'https://connect.facebook.net/en_US/fbevents.js');
|
n.loaded = !0
|
||||||
fbq('init', '541491674547104');
|
n.version = '2.0'
|
||||||
fbq('track', 'PageView');
|
n.queue = []
|
||||||
|
t = b.createElement(e)
|
||||||
|
t.async = !0
|
||||||
|
t.src = v
|
||||||
|
s = b.getElementsByTagName(e)[0]
|
||||||
|
s.parentNode.insertBefore(t, s)
|
||||||
|
})(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js')
|
||||||
|
fbq('init', '541491674547104')
|
||||||
|
fbq('track', 'PageView')
|
||||||
</script>
|
</script>
|
||||||
<noscript><img height="1" width="1" style="display:none"
|
<noscript><img height="1" width="1" style="display: none" src="https://www.facebook.com/tr?id=541491674547104&ev=PageView&noscript=1" /></noscript>
|
||||||
src="https://www.facebook.com/tr?id=541491674547104&ev=PageView&noscript=1"
|
|
||||||
/></noscript>
|
|
||||||
<!-- End Meta Pixel Code -->
|
<!-- End Meta Pixel Code -->
|
||||||
|
|
||||||
<!-- Google tag (gtag.js) -->
|
<!-- Google tag (gtag.js) -->
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-8RCL0H1Q7V"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-8RCL0H1Q7V"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || []
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag() {
|
||||||
gtag('js', new Date());
|
dataLayer.push(arguments)
|
||||||
|
}
|
||||||
|
gtag('js', new Date())
|
||||||
|
|
||||||
gtag('config', 'G-8RCL0H1Q7V');
|
gtag('config', 'G-8RCL0H1Q7V')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Google Tag Manager -->
|
<!-- Google Tag Manager -->
|
||||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
<script>
|
||||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
;(function (w, d, s, l, i) {
|
||||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
w[l] = w[l] || []
|
||||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' })
|
||||||
})(window,document,'script','dataLayer','GTM-WLHVPKW');</script>
|
var f = d.getElementsByTagName(s)[0],
|
||||||
|
j = d.createElement(s),
|
||||||
|
dl = l != 'dataLayer' ? '&l=' + l : ''
|
||||||
|
j.async = true
|
||||||
|
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl
|
||||||
|
f.parentNode.insertBefore(j, f)
|
||||||
|
})(window, document, 'script', 'dataLayer', 'GTM-WLHVPKW')
|
||||||
|
</script>
|
||||||
<!-- End Google Tag Manager -->
|
<!-- End Google Tag Manager -->
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Google Tag Manager (noscript) -->
|
<!-- Google Tag Manager (noscript) -->
|
||||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WLHVPKW"
|
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WLHVPKW" height="0" width="0" style="display: none; visibility: hidden"></iframe></noscript>
|
||||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
|
||||||
<!-- End Google Tag Manager (noscript) -->
|
<!-- End Google Tag Manager (noscript) -->
|
||||||
|
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -39,9 +39,10 @@
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"flowbite-svelte": "^0.28.11",
|
"flowbite-svelte": "^0.28.11",
|
||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
"sass": "^1.56.1",
|
"sass": "^1.59.3",
|
||||||
"svelte": "^3.52.0",
|
"svelte": "^3.52.0",
|
||||||
"svelte-preprocess": "^4.10.7",
|
"svelte-preprocess": "^4.10.7",
|
||||||
|
"svelte-preprocess-sass": "^2.0.1",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^3.2.3"
|
"vite": "^3.2.3"
|
||||||
|
@ -5823,6 +5824,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svelte-preprocess-filter": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-preprocess-filter/-/svelte-preprocess-filter-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-92innv59nyEx24xbfcSurB5ocwC8qFdDtGli/JVMHzJsxyvV2yjQKIcbUqU9VIV5mKUWO2PoY93nncS2yF4ULQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/svelte-preprocess-sass": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-preprocess-sass/-/svelte-preprocess-sass-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-0y4FjRsRWcN7rJeNJnSfZ7LVAz6S7/j9Dg24XFRelr/rjMMjXORdEvXy4r38fUYmyk9Y7yjwlHCiqyGxMHhEbg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"svelte-preprocess-filter": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"sass": "^1.35.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/svelte-preprocess/node_modules/magic-string": {
|
"node_modules/svelte-preprocess/node_modules/magic-string": {
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
|
@ -10417,6 +10436,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"svelte-preprocess-filter": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-preprocess-filter/-/svelte-preprocess-filter-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-92innv59nyEx24xbfcSurB5ocwC8qFdDtGli/JVMHzJsxyvV2yjQKIcbUqU9VIV5mKUWO2PoY93nncS2yF4ULQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"svelte-preprocess-sass": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-preprocess-sass/-/svelte-preprocess-sass-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-0y4FjRsRWcN7rJeNJnSfZ7LVAz6S7/j9Dg24XFRelr/rjMMjXORdEvXy4r38fUYmyk9Y7yjwlHCiqyGxMHhEbg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"svelte-preprocess-filter": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"svelte-routing": {
|
"svelte-routing": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-1.6.0.tgz",
|
||||||
|
|
|
@ -22,9 +22,10 @@
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"flowbite-svelte": "^0.28.11",
|
"flowbite-svelte": "^0.28.11",
|
||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
"sass": "^1.56.1",
|
"sass": "^1.59.3",
|
||||||
"svelte": "^3.52.0",
|
"svelte": "^3.52.0",
|
||||||
"svelte-preprocess": "^4.10.7",
|
"svelte-preprocess": "^4.10.7",
|
||||||
|
"svelte-preprocess-sass": "^2.0.1",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^3.2.3"
|
"vite": "^3.2.3"
|
||||||
|
|
|
@ -2,5 +2,5 @@ const autoprefixer = require('autoprefixer')
|
||||||
const tailwind = require('tailwindcss')
|
const tailwind = require('tailwindcss')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [tailwind(), autoprefixer()],
|
plugins: [tailwind(), autoprefixer()]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
Copyright 2013 The Redacted Project Authors (https://github.com/christiannaths/redacted-font)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,93 @@
|
||||||
|
Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,53 +1,4 @@
|
||||||
// This is the service worker with the combined offline experience (Offline page + Offline copy of pages)
|
|
||||||
import { workbox } from 'https://storage.googleapis.com/workbox-cdn/releases/5.1.4/workbox-sw.js'
|
|
||||||
|
|
||||||
const CACHE = 'pwabuilder-offline-page'
|
self.addEventListener('install', (e) => { })
|
||||||
|
|
||||||
importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.4/workbox-sw.js')
|
self.addEventListener('fetch', (e) => { })
|
||||||
// TODO: replace the following with the correct offline fallback page i.e.: const offlineFallbackPage = "offline.html";
|
|
||||||
const offlineFallbackPage = '/offline.html'
|
|
||||||
|
|
||||||
self.addEventListener('message', (event) => {
|
|
||||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
||||||
self.skipWaiting()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
self.addEventListener('install', async (event) => {
|
|
||||||
event.waitUntil(
|
|
||||||
caches.open(CACHE)
|
|
||||||
.then((cache) => cache.add(offlineFallbackPage))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (workbox.navigationPreload.isSupported()) {
|
|
||||||
workbox.navigationPreload.enable()
|
|
||||||
}
|
|
||||||
|
|
||||||
workbox.routing.registerRoute(
|
|
||||||
/\/*/,
|
|
||||||
new workbox.strategies.StaleWhileRevalidate({
|
|
||||||
cacheName: CACHE
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
self.addEventListener('fetch', (event) => {
|
|
||||||
if (event.request.mode === 'navigate') {
|
|
||||||
event.respondWith((async () => {
|
|
||||||
try {
|
|
||||||
const preloadResp = await event.preloadResponse
|
|
||||||
|
|
||||||
if (preloadResp) {
|
|
||||||
return preloadResp
|
|
||||||
}
|
|
||||||
|
|
||||||
const networkResp = await fetch(event.request)
|
|
||||||
return networkResp
|
|
||||||
} catch (error) {
|
|
||||||
const cache = await caches.open(CACHE)
|
|
||||||
const cachedResp = await cache.match(offlineFallbackPage)
|
|
||||||
return cachedResp
|
|
||||||
}
|
|
||||||
})())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,4 +1,55 @@
|
||||||
|
|
||||||
//self.addEventListener("install", (e) => { })
|
// This is the service worker with the combined offline experience (Offline page + Offline copy of pages)
|
||||||
|
import workbox from 'workbox-sw'
|
||||||
|
|
||||||
//self.addEventListener('fetch', (e) => { })
|
const CACHE = 'pwabuilder-offline-page'
|
||||||
|
|
||||||
|
importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.4/workbox-sw.js')
|
||||||
|
|
||||||
|
// TODO: replace the following with the correct offline fallback page i.e.: const offlineFallbackPage = "offline.html";
|
||||||
|
const offlineFallbackPage = '/offline.html'
|
||||||
|
|
||||||
|
self.addEventListener('message', (event) => {
|
||||||
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||||
|
self.skipWaiting()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener('install', async (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE)
|
||||||
|
.then((cache) => cache.add(offlineFallbackPage))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (workbox.navigationPreload.isSupported()) {
|
||||||
|
workbox.navigationPreload.enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
workbox.routing.registerRoute(
|
||||||
|
/\/*/,
|
||||||
|
new workbox.strategies.StaleWhileRevalidate({
|
||||||
|
cacheName: CACHE
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
self.addEventListener('fetch', (event) => {
|
||||||
|
if (event.request.mode === 'navigate') {
|
||||||
|
event.respondWith((async () => {
|
||||||
|
try {
|
||||||
|
const preloadResp = await event.preloadResponse
|
||||||
|
|
||||||
|
if (preloadResp) {
|
||||||
|
return preloadResp
|
||||||
|
}
|
||||||
|
|
||||||
|
const networkResp = await fetch(event.request)
|
||||||
|
return networkResp
|
||||||
|
} catch (error) {
|
||||||
|
const cache = await caches.open(CACHE)
|
||||||
|
const cachedResp = await cache.match(offlineFallbackPage)
|
||||||
|
return cachedResp
|
||||||
|
}
|
||||||
|
})())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -13,46 +13,12 @@
|
||||||
loading={Loading}
|
loading={Loading}
|
||||||
error={Error}
|
error={Error}
|
||||||
routes={[
|
routes={[
|
||||||
|
//experience
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/:gameurl',
|
||||||
component: () => import('$routes/homepage/homepage.svelte'),
|
component: () => import('$routes/game/experience.svelte'),
|
||||||
layout: NavigationBarLayout,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/error',
|
|
||||||
component: () => import('$routes/error.svelte'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/scanner',
|
|
||||||
component: () => import('$routes/qrscanner/qrscanner.svelte'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/test',
|
|
||||||
component: () => import('$routes/test.svelte'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/map',
|
|
||||||
component: () => import('$root/src/routes/map/map.svelte'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/explore',
|
|
||||||
component: () => import('$routes/explore/explore.svelte'),
|
|
||||||
layout: NavigationBarLayout,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/profile/',
|
|
||||||
component: () => import('$src/__error.svelte'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/profile/:erantId',
|
|
||||||
component: () => import('$routes/profile/profile.svelte'),
|
|
||||||
layout: NavigationBarLayout,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/profile/setting/:function',
|
|
||||||
component: () => import('$routes/profile/profile-functions.svelte'),
|
|
||||||
layout: ArrowBackLayout,
|
|
||||||
},
|
},
|
||||||
|
//authorization
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
component: () => import('$routes/authorization/login/log_in.svelte'),
|
component: () => import('$routes/authorization/login/log_in.svelte'),
|
||||||
|
@ -65,7 +31,6 @@
|
||||||
path: '/register/failed',
|
path: '/register/failed',
|
||||||
component: () => import('$routes/authorization/register/registerFailed.svelte'),
|
component: () => import('$routes/authorization/register/registerFailed.svelte'),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: '/register/emailverification/:erantId',
|
path: '/register/emailverification/:erantId',
|
||||||
component: () => import('$routes/authorization/register/emailVerification.svelte'),
|
component: () => import('$routes/authorization/register/emailVerification.svelte'),
|
||||||
|
@ -75,9 +40,54 @@
|
||||||
component: () => import('$routes/authorization/register/createAccount.svelte'),
|
component: () => import('$routes/authorization/register/createAccount.svelte'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/:gameurl',
|
path: '/forgot-pswd',
|
||||||
component: () => import('$routes/game/game.svelte'),
|
component: () => import('$routes/authorization/forgottonPassword/forgot-pswd.svelte'),
|
||||||
},
|
},
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('$routes/homepage/homepage.svelte'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/explore',
|
||||||
|
component: () => import('$routes/explore/explore.svelte'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/profile/:erantId',
|
||||||
|
component: () => import('$routes/profile/profile.svelte'),
|
||||||
|
},
|
||||||
|
].map((routes) => {
|
||||||
|
return { ...routes, layout: NavigationBarLayout }
|
||||||
|
}),
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
path: '/profile/setting/:function',
|
||||||
|
component: () => import('$routes/profile/profile-functions.svelte'),
|
||||||
|
},
|
||||||
|
].map((routes) => {
|
||||||
|
return { ...routes, layout: ArrowBackLayout }
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
path: '/error',
|
||||||
|
component: () => import('$routes/error.svelte'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/scanner',
|
||||||
|
component: () => import('$routes/qrscanner/qrscanner.svelte'),
|
||||||
|
},
|
||||||
|
/*{
|
||||||
|
path: '/test',
|
||||||
|
component: () => import('$routes/test.svelte'),
|
||||||
|
},*/
|
||||||
|
{
|
||||||
|
path: '/map',
|
||||||
|
component: () => import('$root/src/routes/map/map.svelte'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/profile/',
|
||||||
|
component: () => import('$src/__error.svelte'),
|
||||||
|
},
|
||||||
|
//policy
|
||||||
{
|
{
|
||||||
path: '/terms-and-conditions',
|
path: '/terms-and-conditions',
|
||||||
component: () => import('$routes/legal/terms-and-conditions.svelte'),
|
component: () => import('$routes/legal/terms-and-conditions.svelte'),
|
||||||
|
@ -89,10 +99,6 @@
|
||||||
{
|
{
|
||||||
path: '/cookie-policy',
|
path: '/cookie-policy',
|
||||||
component: () => import('$routes/legal/cookie-policy.svelte'),
|
component: () => import('$routes/legal/cookie-policy.svelte'),
|
||||||
},
|
}
|
||||||
{
|
|
||||||
path: '/forgot-pswd',
|
|
||||||
component: () => import('$routes/authorization/forgottonPassword/forgot-pswd.svelte'),
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
export type CheckPoint = {
|
export type CheckPoint = {
|
||||||
$id: string
|
$id: string
|
||||||
|
$collectionId: string
|
||||||
|
$createdAt: string
|
||||||
|
$databaseId: string
|
||||||
|
$permissions: string
|
||||||
|
$updatedAt: string
|
||||||
CPAfter: string
|
CPAfter: string
|
||||||
CPAnswerID: string
|
CPAnswerID: string
|
||||||
CPHint: string
|
CPHint: string
|
||||||
|
@ -13,8 +18,14 @@ export type CheckPoint = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Experience = {
|
export type Experience = {
|
||||||
|
$id: string
|
||||||
|
$collectionId: string
|
||||||
|
$createdAt: string
|
||||||
|
$databaseId: string
|
||||||
|
$permissions: string
|
||||||
|
$updatedAt: string
|
||||||
ExpApproved: boolean
|
ExpApproved: boolean
|
||||||
ExpCpsID: string[]
|
ExpCPsID: string[]
|
||||||
ExpCategory?: string
|
ExpCategory?: string
|
||||||
ExpEnd0: string
|
ExpEnd0: string
|
||||||
ExpEnd60: string
|
ExpEnd60: string
|
||||||
|
@ -27,5 +38,6 @@ export type Experience = {
|
||||||
ExpTestingCode: string
|
ExpTestingCode: string
|
||||||
ExpURL: string
|
ExpURL: string
|
||||||
UserID: string
|
UserID: string
|
||||||
checkPoint: Array<CheckPoint>
|
checkPoints: Array<CheckPoint>
|
||||||
|
rating: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,16 @@ import { Collection } from './appwrite'
|
||||||
|
|
||||||
const experiences = new Collection('63cef30d6da945dd4250', '63cef4bd210fdf2e5888')
|
const experiences = new Collection('63cef30d6da945dd4250', '63cef4bd210fdf2e5888')
|
||||||
const users = new Collection('63ded6c18e8493bffc83', 'Users')
|
const users = new Collection('63ded6c18e8493bffc83', 'Users')
|
||||||
|
const interests = new Collection('6417cf1de159d094b370', '6417cf29f2118829b3b4')
|
||||||
|
const travel_with = new Collection('6417cf1de159d094b370', '6417d0429843609a2f49')
|
||||||
|
const recommended_by = new Collection('6417cf1de159d094b370', '6417d00e40701375978b')
|
||||||
|
const usersAnswers = new Collection('63cef30d6da945dd4250', 'users-answers')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
experiences,
|
experiences,
|
||||||
users,
|
users,
|
||||||
|
interests,
|
||||||
|
travel_with,
|
||||||
|
recommended_by,
|
||||||
|
usersAnswers,
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,11 @@
|
||||||
color: white;
|
color: white;
|
||||||
background-color: rgb(66, 99, 235);
|
background-color: rgb(66, 99, 235);
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(107 114 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(66, 99, 235, 0.8);
|
background-color: rgba(66, 99, 235, 0.8);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<!-- This component is imported to display a category to (de)select-->
|
||||||
|
<script lang="ts">
|
||||||
|
export let name;
|
||||||
|
export let image;
|
||||||
|
export let selected = false;
|
||||||
|
export let onToggle = () => {};
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
onToggle();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="category" class:selected={selected} on:click={handleClick}>
|
||||||
|
<div class="image">
|
||||||
|
<img src={image} alt={name} />
|
||||||
|
</div>
|
||||||
|
<div class="name">{name}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.category {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 120px;
|
||||||
|
margin: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
margin-top: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border: 2px solid blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let preferences;
|
||||||
|
export let onPreferenceSelect;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="category-2-in-row">
|
||||||
|
{#each preferences as preference}
|
||||||
|
<div
|
||||||
|
class="preference"
|
||||||
|
style="background-image: url({preference.img})"
|
||||||
|
on:click={() => onPreferenceSelect(preference)}
|
||||||
|
>
|
||||||
|
<span>{preference.name}</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import collections from '$lib/collections'
|
import collections from '$lib/collections'
|
||||||
import { Query } from 'appwrite'
|
import { Query } from 'appwrite'
|
||||||
import { navigate } from 'svelte-routing'
|
import { navigate } from 'svelte-routing'
|
||||||
|
import Category from "$lib/components/Categories/category.svelte";
|
||||||
|
|
||||||
export let current_state = 1
|
export let current_state = 1
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Img } from "flowbite-svelte"
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
|
||||||
|
export let name;
|
||||||
|
export let img;
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="preference">
|
||||||
|
<div
|
||||||
|
on:click={() => dispatch('selected')}
|
||||||
|
>
|
||||||
|
<Img alt="sample 1" size="max-w-lg" class="rounded-lg abcd" src={img}/>
|
||||||
|
<span>{name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div
|
||||||
|
&.selected {
|
||||||
|
div {
|
||||||
|
border: 1px dashed #4263eb;
|
||||||
|
p {
|
||||||
|
color: #4263eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { each } from "svelte/internal"
|
||||||
|
import { Html } from "./Meta"
|
||||||
|
|
||||||
|
|
||||||
|
export let headline:string= "";
|
||||||
|
export let options:any[] = [];
|
||||||
|
export let on_return:Function = () => {};
|
||||||
|
export let default_ans:string = undefined
|
||||||
|
|
||||||
|
let selected:string = default_ans
|
||||||
|
|
||||||
|
function change_answer(){
|
||||||
|
const container = (event.currentTarget as HTMLInputElement).parentElement.parentElement
|
||||||
|
|
||||||
|
selected = (container.querySelector("input:checked") as HTMLInputElement).parentElement.querySelector("p").innerText
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel_popUp(){
|
||||||
|
on_return(undefined)
|
||||||
|
}
|
||||||
|
function save_popUp(){
|
||||||
|
on_return(selected)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="pop-up-container">
|
||||||
|
<div class="pop-up">
|
||||||
|
<h1 class="headline">
|
||||||
|
{headline}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="options">
|
||||||
|
{#each options as option}
|
||||||
|
<label class="option">
|
||||||
|
{#if option === selected}
|
||||||
|
<input type="radio" on:change={change_answer} name="input" hidden checked/>
|
||||||
|
{:else}
|
||||||
|
<input type="radio" on:change={change_answer} name="input" hidden/>
|
||||||
|
{/if}
|
||||||
|
<div class="option-content">
|
||||||
|
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.26667 1.66671L6.10667 0.866707C6.04667 0.560041 5.77333 0.333374 5.45333 0.333374H0.666667C0.3 0.333374 0 0.633374 0 1.00004V11C0 11.3667 0.3 11.6667 0.666667 11.6667C1.03333 11.6667 1.33333 11.3667 1.33333 11V7.00004H5.06667L5.22667 7.80004C5.28667 8.11337 5.56 8.33337 5.88 8.33337H9.33333C9.7 8.33337 10 8.03337 10 7.66671V2.33337C10 1.96671 9.7 1.66671 9.33333 1.66671H6.26667Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
<p>{option}</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button on:click={cancel_popUp} class="cancel">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button on:click={save_popUp} class="save">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pop-up-container{
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9999;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
backdrop-filter: blur(5px) brightness(60%);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.pop-up{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 24px;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
width: 80%;
|
||||||
|
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.headline{
|
||||||
|
font-family: 'Lato';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 22px;
|
||||||
|
|
||||||
|
letter-spacing: 0.004em;
|
||||||
|
|
||||||
|
/* Black */
|
||||||
|
|
||||||
|
color: #212529;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap:16px;
|
||||||
|
.option{
|
||||||
|
.option-content{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
gap: 7px;
|
||||||
|
|
||||||
|
background: #F7F5FF;
|
||||||
|
|
||||||
|
|
||||||
|
svg, p{
|
||||||
|
color: #4263EB;
|
||||||
|
path{
|
||||||
|
fill: #4263EB
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
&:has(input:checked){
|
||||||
|
.option-content{
|
||||||
|
background:#4263EB;
|
||||||
|
|
||||||
|
svg, p{
|
||||||
|
color:#F7F5FF;
|
||||||
|
path{
|
||||||
|
fill: #F7F5FF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions{
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 16px;
|
||||||
|
button {
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: center;
|
||||||
|
/* label medium */
|
||||||
|
|
||||||
|
font-family: 'Work Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
/* identical to box height, or 143% */
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
|
||||||
|
&:hover{
|
||||||
|
opacity: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.save{
|
||||||
|
background: #4263EB;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.cancel{
|
||||||
|
color: #4263EB;
|
||||||
|
background: #F7F5FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
|
@ -34,8 +34,8 @@
|
||||||
height: auto;
|
height: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 25%;
|
top: -10%;
|
||||||
border-radius: 70px 70px 0 0;
|
border-radius: 70px 70px 0 0;
|
||||||
padding: 52px;
|
padding: 52px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
position: absolute;
|
position: relative;
|
||||||
}
|
}
|
||||||
.shareButton {
|
.shareButton {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
import FooterItem from '../Common/NavBar_Item.svelte'
|
import FooterItem from '../Common/NavBar_Item.svelte'
|
||||||
|
|
||||||
$: [userInfo] = collections.users.getDocument([Query.equal('userId', $user?.$id || '')])
|
$: [userInfo] = collections.users.getDocument([Query.equal('userId', $user?.$id || '')])
|
||||||
$: console.log($user)
|
|
||||||
|
|
||||||
$: items = [
|
$: items = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,21 +1,37 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import GeolocateControl from '@beyonk/svelte-mapbox/src/lib/map/controls/GeolocateControl.svelte'
|
import GeolocateControl from '@beyonk/svelte-mapbox/src/lib/map/controls/GeolocateControl.svelte'
|
||||||
import Map from './Map.svelte'
|
import Map from './Map.svelte'
|
||||||
import { navigate } from 'svelte-routing'
|
import { navigate } from 'svelte-routing'
|
||||||
import NavigationBarLayout from '../Layouts/NavigationBarLayout.svelte'
|
import NavigationBarLayout from '../Layouts/NavigationBarLayout.svelte'
|
||||||
import { createEventDispatcher, onMount } from 'svelte'
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
|
import LocationRequest from './LocationRequest.svelte'
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let center = null
|
export let center = null
|
||||||
|
|
||||||
export let mapComponent = null
|
export let mapComponent = null
|
||||||
export let user = { lat: null, lng: null }
|
export let userLocation = { lat: 0, lng: 0 }
|
||||||
|
|
||||||
|
$: console.log(userLocation, center)
|
||||||
|
|
||||||
let className = ''
|
let className = ''
|
||||||
export { className as class }
|
export { className as class }
|
||||||
|
/*;(() => {
|
||||||
|
navigator.geolocation.getCurrentPosition(() => {
|
||||||
|
navigator.geolocation.watchPosition((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})()*/
|
||||||
|
|
||||||
if (navigator.geolocation) {
|
navigator.geolocation.getCurrentPosition((position: GeolocationPosition) => {
|
||||||
navigator.geolocation.getCurrentPosition(showPosition, () => dispatch('locationFailed'))
|
if (!userLocation) userLocation = { lat: position.coords.latitude, lng: position.coords.longitude }
|
||||||
|
if (!center) center = { lat: position.coords.latitude, lng: position.coords.longitude }
|
||||||
|
}) //at mapbox this is required dont know why
|
||||||
|
|
||||||
|
$: if (!center) center = userLocation
|
||||||
|
/* if (navigator.geolocation) {
|
||||||
|
navigator.geolocation.watchPosition(showPosition, () => dispatch('locationFailed'))
|
||||||
} else {
|
} else {
|
||||||
alert("Can't load your location!")
|
alert("Can't load your location!")
|
||||||
}
|
}
|
||||||
|
@ -23,19 +39,27 @@
|
||||||
user.lat = position.coords.latitude
|
user.lat = position.coords.latitude
|
||||||
user.lng = position.coords.longitude
|
user.lng = position.coords.longitude
|
||||||
if (!center) center = user
|
if (!center) center = user
|
||||||
}
|
}*/
|
||||||
|
|
||||||
const userCenter = () => document.getElementsByClassName('mapboxgl-ctrl-geolocate')[0].click()
|
const userCenter = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
document.getElementsByClassName('mapboxgl-ctrl-geolocate')[0].click()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<LocationRequest />
|
||||||
|
|
||||||
<NavigationBarLayout>
|
<NavigationBarLayout>
|
||||||
{#if center}
|
{#if center}
|
||||||
<Map on:ready={() => setTimeout(() => userCenter(), 40)} {center} bind:mapComponent class={className} on:move>
|
<Map on:ready={() => setTimeout(() => userCenter(), 100)} {center} bind:mapComponent class={className} on:move>
|
||||||
<slot />
|
<slot />
|
||||||
<GeolocateControl
|
<GeolocateControl
|
||||||
|
options={{ trackUserLocation: true }}
|
||||||
|
on:trackuserlocationstart={() => {}}
|
||||||
on:geolocate={(e) => {
|
on:geolocate={(e) => {
|
||||||
|
// @ts-ignore
|
||||||
const { latitude, longitude } = e.detail.coords
|
const { latitude, longitude } = e.detail.coords
|
||||||
user = { lat: latitude, lng: longitude }
|
userLocation = { lat: latitude, lng: longitude }
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Map>
|
</Map>
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if granted === false}
|
{#if granted === false}
|
||||||
<Alert class="absolute w-[95%] max-w-[400px] z-50 mt-4" color="green">
|
<div class="w-full justify-center flex absolute">
|
||||||
|
<Alert class="w-[95%] max-w-[400px] z-50 mt-4" color="green">
|
||||||
<span class="text-lg font-medium">This is a info alert</span>
|
<span class="text-lg font-medium">This is a info alert</span>
|
||||||
<div slot="extra">
|
<div slot="extra">
|
||||||
<div class="mt-2 mb-4 text-sm">To advance through your experience you need to enable the location access, without it the app won't work.</div>
|
<div class="mt-2 mb-4 text-sm">To advance through your experience you need to enable the location access, without it the app won't work.</div>
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<!--{#if state === 'granted'}
|
<!--{#if state === 'granted'}
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
|
|
||||||
export let radius = false
|
export let radius = false
|
||||||
export let center: { lng: number; lat: number } = { lng: 0, lat: 0 }
|
export let center: { lng: number; lat: number } = { lng: 0, lat: 0 }
|
||||||
$: console.log(center)
|
|
||||||
|
|
||||||
/*export const geo = (e) => {
|
/*export const geo = (e) => {
|
||||||
geolocateControl.dispatchEvent('geolocate')
|
geolocateControl.dispatchEvent('geolocate')
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
mapComponent.setCenter([center.lng, center.lat], 14)
|
mapComponent.setCenter([center.lng, center.lat], 14)
|
||||||
dispatch('ready')
|
dispatch('ready')
|
||||||
}}
|
}}
|
||||||
|
on:recentre={(e) => {}}
|
||||||
zoom={14}
|
zoom={14}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -6,15 +6,15 @@
|
||||||
|
|
||||||
export let lat = 0
|
export let lat = 0
|
||||||
export let lng = 0
|
export let lng = 0
|
||||||
export let user = { lat: 0, lng: 0 }
|
export let userLocation = { lat: 0, lng: 0 }
|
||||||
export let popup: string | null = null
|
export let popup: string | null = null
|
||||||
|
|
||||||
export let round = (1 / 110.574 / 1000) * 12 //cca 12m nutno pozměnit!!!!!!!!!! tento komentář nemazat
|
export let round = (1 / 110.574 / 1000) * 12 //cca 12m nutno pozměnit!!!!!!!!!! tento komentář nemazat
|
||||||
let Mlat = [lat - round, lat + round]
|
let Mlat = [lat - round, lat + round]
|
||||||
let Mlng = [lng - round, lng + round]
|
let Mlng = [lng - round, lng + round]
|
||||||
$: isIn = user ? user.lat > Mlat[0] && user.lat < Mlat[1] && user.lng > Mlng[0] && user.lng < Mlng[1] : null
|
$: isIn = userLocation ? userLocation.lat > Mlat[0] && userLocation.lat < Mlat[1] && userLocation.lng > Mlng[0] && userLocation.lng < Mlng[1] : null
|
||||||
|
|
||||||
$: isIn && dispatch('enter', { lat, lng, user })
|
$: isIn && dispatch('enter', { lat, lng, userLocation })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Marker popup={false} {lat} {lng}>
|
<Marker popup={false} {lat} {lng}>
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Progressbar } from 'flowbite-svelte'
|
||||||
|
|
||||||
|
export let max: number
|
||||||
|
export let progress: number
|
||||||
|
export let showWrittenProgress = false
|
||||||
|
export let labelInside: boolean = false
|
||||||
|
|
||||||
|
$: value = JSON.stringify((progress / max) * 100)
|
||||||
|
|
||||||
|
let className = ''
|
||||||
|
export { className as class }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={`w-full h-auto flex flex-wrap flex-row justify-center ${className}`}>
|
||||||
|
{#if showWrittenProgress}
|
||||||
|
<span>{progress}/{max}</span>
|
||||||
|
{/if}
|
||||||
|
<Progressbar {labelInside} progress={value} />
|
||||||
|
</div>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { ComponentType, SvelteComponentTyped } from 'svelte'
|
||||||
|
export let path: string
|
||||||
|
export let component: () => Promise<any>
|
||||||
|
export let loading: ComponentType<SvelteComponentTyped<any>> | null = null
|
||||||
|
import { Route } from '$lib/router'
|
||||||
|
import LazyRouteGuard from './LazyRouteGuard.svelte'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Route {path} let:location let:params>
|
||||||
|
<LazyRouteGuard {location} {params} {component} {loading} />
|
||||||
|
</Route>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { RouteLocation } from 'svelte-routing/types/Route'
|
||||||
|
import Redirect from './Redirect.svelte'
|
||||||
|
export let fallback: string
|
||||||
|
export let allow: boolean
|
||||||
|
export let location: RouteLocation
|
||||||
|
</script>
|
||||||
|
{#if allow}
|
||||||
|
<slot />
|
||||||
|
{:else}
|
||||||
|
<Redirect to={fallback} replace state={{ from: location.pathname }} />
|
||||||
|
{/if}
|
|
@ -33,3 +33,4 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</Router>
|
</Router>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { ComponentType, SvelteComponentTyped } from 'svelte'
|
||||||
|
|
||||||
|
export interface Route {
|
||||||
|
component: () => Promise<any>,
|
||||||
|
path: string,
|
||||||
|
layout?: ComponentType<SvelteComponentTyped<any>>,
|
||||||
|
loading?: ComponentType<SvelteComponentTyped<any>>,
|
||||||
|
before?: () => any,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RouteDefinition {
|
||||||
|
component: () => Promise<any>,
|
||||||
|
path: string,
|
||||||
|
layout: ComponentType<SvelteComponentTyped<any>> | null,
|
||||||
|
loading: ComponentType<SvelteComponentTyped<any>> | null,
|
||||||
|
before: () => any | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RouteConfig {
|
||||||
|
routes: Route[],
|
||||||
|
layout?: ComponentType<SvelteComponentTyped<any>> | null,
|
||||||
|
loading?: ComponentType<SvelteComponentTyped<any>> | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const defineRoutes = (config: RouteConfig): RouteDefinition[] => {
|
||||||
|
return config.routes.map(route => ({ ...route, layout: route?.layout ?? config?.layout ?? null, loading: route?.loading ?? config?.loading ?? null, before: route?.before ?? null }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineRoutes
|
|
@ -1,55 +0,0 @@
|
||||||
async function send({ method, path, body, token, headers }) {
|
|
||||||
const opts = { method, headers: new Headers(), mode: 'cors' }
|
|
||||||
opts.headers.append('Content-Type', 'application/json')
|
|
||||||
opts.headers.append('Access-Control-Allow-Origin', '*')
|
|
||||||
|
|
||||||
if (body) {
|
|
||||||
opts.body = JSON.stringify(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers) {
|
|
||||||
for (const [k, v] of Object.entries(headers)) {
|
|
||||||
opts.headers.append(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token) {
|
|
||||||
opts.headers['Authorization'] = `Bearer ${token}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = fetch(path, opts)
|
|
||||||
.then(async (r) => {
|
|
||||||
if (r.status >= 200 && r.status < 400) {
|
|
||||||
return await r.text()
|
|
||||||
} else {
|
|
||||||
return await r.text()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((str) => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(str)
|
|
||||||
} catch (err) {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get(path, token = null) {
|
|
||||||
return send({ method: 'GET', path, body: null, token })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function del(path, token = null) {
|
|
||||||
return send({ method: 'DELETE', path, body: null, token })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function post(path, body, token = null) {
|
|
||||||
return send({ method: 'POST', path, body, token })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function put(path, body, token = null, headers = []) {
|
|
||||||
return send({ method: 'PUT', path, body, token, headers })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hostName = 'https://erant.cz/api'
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
import { databases, user as userStore } from '$lib/appwrite'
|
||||||
|
import { Account, Models, Permission, Query, Role } from 'appwrite'
|
||||||
|
import database from 'svelte-appwrite-client/src/lib/database'
|
||||||
|
import { getLocationDataFromLatAndLong } from '../locations'
|
||||||
|
import { writable } from 'svelte/store'
|
||||||
|
import { CheckPoint, Experience } from '$lib/TStypes/experiences'
|
||||||
|
import collections from '$lib/collections'
|
||||||
|
|
||||||
|
export type AnswerState = 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
||||||
|
|
||||||
|
let user: { $id: string }
|
||||||
|
userStore.subscribe((res) => (user = res))
|
||||||
|
|
||||||
|
//Loading of checkpoints and rating is done in the same function to prevent multiple requests to the database
|
||||||
|
export const load = async (pathName: string, previewQuestionsCount?: number, preview?: Function) => {
|
||||||
|
const checkPoints: Array<CheckPoint> = []
|
||||||
|
// @ts-ignore
|
||||||
|
const experience: Experience = (await databases.listDocuments('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpURL', pathName)])).documents[0]
|
||||||
|
|
||||||
|
const checkPointsIds = experience.ExpCPsID
|
||||||
|
const rating = await getRating(experience.$id)
|
||||||
|
|
||||||
|
for (const checkPointId of checkPointsIds) {
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
checkPoints.push(await databases.getDocument('63cef30d6da945dd4250', '63cef84d908acf805758', checkPointId))
|
||||||
|
if (checkPointsIds.indexOf(checkPointId) === previewQuestionsCount - 1) {
|
||||||
|
experience['rating'] = rating
|
||||||
|
experience['checkPoints'] = checkPoints
|
||||||
|
preview(experience)
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
experience['rating'] = rating
|
||||||
|
experience['checkPoints'] = checkPoints
|
||||||
|
|
||||||
|
return experience
|
||||||
|
}
|
||||||
|
//Fetch of Rating
|
||||||
|
const getRating = async (expId: string) => {
|
||||||
|
const { documents, total } = await databases.listDocuments('63cef30d6da945dd4250', '63ee6353ebb174cf815d', [Query.equal('ExpID', expId)])
|
||||||
|
const sum = documents.reduce((accumulator, rating) => accumulator + rating.ExpUserRating, 0)
|
||||||
|
return sum / total || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch of answers to the checkpoints
|
||||||
|
export const answer = async (experienceId: string, checkPointId: string, answer: any) => {
|
||||||
|
try {
|
||||||
|
const checkPoint = await databases.getDocument('63cef30d6da945dd4250', '63cef84d908acf805758', checkPointId)
|
||||||
|
console.log({ checkPoint })
|
||||||
|
const { CPType, CPAnswerID } = checkPoint
|
||||||
|
const correctAnswer = CPType !== 'INFO' ? (await databases.getDocument('63cef30d6da945dd4250', '63dd5c2b764061e40025', CPAnswerID)).CPAnswer : true
|
||||||
|
|
||||||
|
let correct: boolean = false
|
||||||
|
|
||||||
|
if (CPType === 'CHECKBOX') correct = JSON.stringify(answer) === JSON.stringify(correctAnswer)
|
||||||
|
if (CPType === 'RADIO' || CPType === 'NUMBER' || CPType === 'TEXT') correct = answer === correctAnswer[0]
|
||||||
|
if (CPType === 'INFO') correct = false
|
||||||
|
await saveAnswerIntoDatabase(experienceId, checkPointId, correct)
|
||||||
|
|
||||||
|
return correct
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error)
|
||||||
|
|
||||||
|
//operation only for sveltekit 401, 500, 403
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveAnswerIntoDatabase = async (experienceId: string, checkPointId: string, correct: boolean) => {
|
||||||
|
const previousAttemptDocument = await getUserAnswer(user.$id, checkPointId)
|
||||||
|
|
||||||
|
if (!(previousAttemptDocument?.attemptCount === 2)) {
|
||||||
|
if (previousAttemptDocument) {
|
||||||
|
collections.usersAnswers.updateDocument(previousAttemptDocument.$id, {
|
||||||
|
correct,
|
||||||
|
attemptCount: 2,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await collections.usersAnswers.createDocument(
|
||||||
|
{
|
||||||
|
userId: user.$id,
|
||||||
|
checkPoint: checkPointId,
|
||||||
|
correct,
|
||||||
|
experience: experienceId,
|
||||||
|
attemptCount: 1,
|
||||||
|
},
|
||||||
|
[Permission.read(Role.user(user.$id)), Permission.update(Role.user(user.$id))],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const err = new Error('User has already answered.')
|
||||||
|
err['code'] = '409'
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserAnswer = async (userId, checkPointId) => {
|
||||||
|
return (await databases.listDocuments('63cef30d6da945dd4250', 'users-answers', [Query.equal('checkPoint', checkPointId), Query.equal('userId', userId)])).documents[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch of all experiences
|
||||||
|
export const getExperiences = async () => {
|
||||||
|
const experiences = (await databases.listDocuments('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpApproved', true)])).documents
|
||||||
|
let items = []
|
||||||
|
for (const experience of experiences) {
|
||||||
|
items.push({
|
||||||
|
city: (await getLocationDataFromLatAndLong(experience.ExpLocation[0], experience.ExpLocation[1])).city,
|
||||||
|
...experience,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
// Fetch of all experiences as store
|
||||||
|
export const getExperiencesAsStore = () => {
|
||||||
|
const store = writable([])
|
||||||
|
const loading = writable<boolean>(true)
|
||||||
|
databases.listDocuments('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpApproved', true)]).then(async ({ documents }) => {
|
||||||
|
let items: Array<any> = []
|
||||||
|
for (const experience of documents) {
|
||||||
|
items.push({
|
||||||
|
city: (await getLocationDataFromLatAndLong(experience.ExpLocation[0], experience.ExpLocation[1])).city,
|
||||||
|
...experience,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loading.set(false)
|
||||||
|
store.set(items)
|
||||||
|
})
|
||||||
|
|
||||||
|
return [store, loading] as const
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserProgress = async (experienceId: string) => {
|
||||||
|
const { documents } = await databases.listDocuments('63cef30d6da945dd4250', 'users-answers', [Query.equal('userId', user.$id), Query.equal('experience', experienceId)])
|
||||||
|
return documents
|
||||||
|
}
|
||||||
|
export const getUserProgressAsStore = (experienceId: string) => {
|
||||||
|
const store = writable<Models.Document[]>([])
|
||||||
|
const loading = writable<boolean>(true)
|
||||||
|
getUserProgress(experienceId).then((documents) => {
|
||||||
|
store.set(documents)
|
||||||
|
loading.set(false)
|
||||||
|
})
|
||||||
|
return [store, loading] as const
|
||||||
|
}
|
||||||
|
|
||||||
|
// !pridat trideni podle kategorie! export const getExperiencesByCategory = async (category: string) => {}
|
|
@ -1,76 +0,0 @@
|
||||||
import { databases } from '$lib/appwrite'
|
|
||||||
import { Query } from 'appwrite'
|
|
||||||
import database from 'svelte-appwrite-client/src/lib/database'
|
|
||||||
import { getLocationDataFromLatAndLong } from '../locations'
|
|
||||||
import { writable } from 'svelte/store'
|
|
||||||
import { Experience } from '$lib/TStypes/experiences'
|
|
||||||
|
|
||||||
export const load = async (pathName: string, previewQuestionsCount?: number, preview?: Function) => {
|
|
||||||
const checkPoints = []
|
|
||||||
const game = (await databases.listDocuments('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpURL', pathName)])).documents[0]
|
|
||||||
|
|
||||||
const checkPointsIds = game.ExpCPsID
|
|
||||||
const rating = await getRating(game.$id)
|
|
||||||
|
|
||||||
for (const checkPointId of checkPointsIds) {
|
|
||||||
try {
|
|
||||||
checkPoints.push(await databases.getDocument('63cef30d6da945dd4250', '63cef84d908acf805758', checkPointId))
|
|
||||||
if (checkPointsIds.indexOf(checkPointId) === previewQuestionsCount - 1) {
|
|
||||||
game['rating'] = rating
|
|
||||||
game['checkPoints'] = checkPoints
|
|
||||||
preview(game)
|
|
||||||
}
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
game['rating'] = rating
|
|
||||||
game['checkPoints'] = checkPoints
|
|
||||||
return game
|
|
||||||
}
|
|
||||||
|
|
||||||
const getRating = async (expId: string) => {
|
|
||||||
const { documents, total } = await databases.listDocuments('63cef30d6da945dd4250', '63ee6353ebb174cf815d', [Query.equal('ExpID', expId)])
|
|
||||||
const sum = documents.reduce((accumulator, rating) => accumulator + rating.ExpUserRating, 0)
|
|
||||||
return sum / total || 0
|
|
||||||
}
|
|
||||||
|
|
||||||
export const answer = async (checkPointId: string, answer: any) => {
|
|
||||||
const checkPoint = await databases.getDocument('63cef30d6da945dd4250', '63cef84d908acf805758', checkPointId)
|
|
||||||
console.log({ checkPoint })
|
|
||||||
const { CPType, CPAnswerID } = checkPoint
|
|
||||||
const correctAnswer = (await databases.getDocument('63cef30d6da945dd4250', '63dd5c2b764061e40025', CPAnswerID)).CPAnswer
|
|
||||||
|
|
||||||
if (CPType === 'CHECKBOX') return JSON.stringify(answer) === JSON.stringify(correctAnswer)
|
|
||||||
if (CPType === 'RADIO' || CPType === 'NUMBER' || CPType === 'TEXT') return answer === correctAnswer[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getExpiriences = async () => {
|
|
||||||
const expiriences = (await databases.listDocuments('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpApproved', true)])).documents
|
|
||||||
let items = []
|
|
||||||
for (const expirience of expiriences) {
|
|
||||||
items.push({
|
|
||||||
city: (await getLocationDataFromLatAndLong(expirience.ExpLocation[0], expirience.ExpLocation[1])).city,
|
|
||||||
...expirience,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getExpiriencesAsStore = () => {
|
|
||||||
const store = writable([])
|
|
||||||
const loading = writable<boolean>(true)
|
|
||||||
databases.listDocuments('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpApproved', true)]).then(async ({ documents }) => {
|
|
||||||
let items: Array<any> = []
|
|
||||||
for (const expirience of documents) {
|
|
||||||
items.push({
|
|
||||||
city: (await getLocationDataFromLatAndLong(expirience.ExpLocation[0], expirience.ExpLocation[1])).city,
|
|
||||||
...expirience,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
loading.set(false)
|
|
||||||
store.set(items)
|
|
||||||
})
|
|
||||||
|
|
||||||
return [store, loading] as const
|
|
||||||
}
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
import { databases } from '$lib/appwrite';
|
||||||
|
import { Query } from 'appwrite';
|
||||||
|
import { writable } from 'svelte/store'
|
||||||
|
|
||||||
|
// typescript interfaces for objects return definitions
|
||||||
|
interface Interest {
|
||||||
|
name: string;
|
||||||
|
img: string;
|
||||||
|
}
|
||||||
|
interface TravelBuddies {
|
||||||
|
name: string;
|
||||||
|
img: string;
|
||||||
|
}
|
||||||
|
interface Recommendations {
|
||||||
|
name: string;
|
||||||
|
img: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a variable to store the selected preferences
|
||||||
|
// let selectedInterests = [];
|
||||||
|
// let selectedTravelBuddies = [];
|
||||||
|
// let selectedRecommendations = [];
|
||||||
|
|
||||||
|
// fetch interests
|
||||||
|
|
||||||
|
export const getInterests = () => {
|
||||||
|
const store = writable<Interest[]>([])
|
||||||
|
databases.listDocuments('6417cf1de159d094b370', '6417cf29f2118829b3b4').then(({ documents }) => {
|
||||||
|
// @ts-ignore
|
||||||
|
store.set(documents)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
return [store] as const
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch travel buddies
|
||||||
|
export const getTravelBuddies = async (): Promise<TravelBuddies[]> => {
|
||||||
|
return ((await databases.listDocuments('6417cf1de159d094b370', '6417d0429843609a2f49')).documents as unknown as TravelBuddies[])};
|
||||||
|
|
||||||
|
// fetch recommendations
|
||||||
|
export const getRecommendations = async (): Promise<Recommendations[]> => {
|
||||||
|
return ((await databases.listDocuments('6417cf1de159d094b370', '6417d00e40701375978b')).documents as unknown as Recommendations[])};
|
||||||
|
|
||||||
|
// add selected preferences to user
|
||||||
|
export const addSelectedPreferences = async (userId, interests, travelBuddies, recommendations) => {
|
||||||
|
const { document: user } = await databases.getDocument('63ded6c18e8493bffc83', 'Users', userId);
|
||||||
|
|
||||||
|
const updatedUser = {
|
||||||
|
...user,
|
||||||
|
userInterests: interests,
|
||||||
|
userTravelBuddy: travelBuddies,
|
||||||
|
userRecommended: recommendations
|
||||||
|
};
|
||||||
|
|
||||||
|
await databases.updateDocument('63ded6c18e8493bffc83', 'Users', user.$id, updatedUser);
|
||||||
|
|
||||||
|
return updatedUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// // Function to update Interests
|
||||||
|
// function updateSelectedInterests(preference) {
|
||||||
|
// if (selectedInterests.includes(preference)) {
|
||||||
|
// selectedInterests = selectedInterests.filter((item) => item !== preference);
|
||||||
|
// } else {
|
||||||
|
// selectedInterests = [...selectedInterests, preference];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // Function to update Travel Buddies
|
||||||
|
// function updateSelectedTravelBuddies(preference) {
|
||||||
|
// if (selectedTravelBuddies.includes(preference)) {
|
||||||
|
// selectedTravelBuddies = selectedTravelBuddies.filter((item) => item !== preference);
|
||||||
|
// } else {
|
||||||
|
// selectedTravelBuddies = [...selectedTravelBuddies, preference];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // Function to update Recommendations
|
||||||
|
// function updateSelectedRecommendations(preference) {
|
||||||
|
// if (selectedRecommendations.includes(preference)) {
|
||||||
|
// selectedRecommendations = selectedRecommendations.filter((item) => item !== preference);
|
||||||
|
// } else {
|
||||||
|
// selectedRecommendations = [...selectedRecommendations, preference];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
@ -2,7 +2,7 @@ export const getLocationDataFromLatAndLong = async (latitude: number, longitude:
|
||||||
const response = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=json`)
|
const response = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=json`)
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
return {
|
return {
|
||||||
city: data.address.town || data.address.city || data.address.village,
|
city: data?.address?.town || data?.address?.city || data?.address?.village,
|
||||||
state: data.address.country,
|
state: data?.address?.country,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
export default (question, questionType) => {
|
|
||||||
switch (questionType.toLowerCase()) {
|
|
||||||
case 'text':
|
|
||||||
return question
|
|
||||||
case 'number':
|
|
||||||
return Number.parseFloat(question)
|
|
||||||
case 'choice':
|
|
||||||
return question.split(/;\s*/).map((item) => ({
|
|
||||||
label: item.startsWith('*') ? item.substring(1) : item,
|
|
||||||
value: item.startsWith('*') ? true : false,
|
|
||||||
}))
|
|
||||||
default:
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"page": {
|
||||||
|
"home": {
|
||||||
|
"title": "Discover the best Travel Experiences"
|
||||||
|
},
|
||||||
|
"Map": {
|
||||||
|
"title": "Map the best Travel Experiences"
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"title": "Register to the best Travel Experiences"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { register, init, getLocaleFromNavigator, isLoading, locale, locales } from 'svelte-i18n'
|
||||||
|
import registers from './languages'
|
||||||
|
|
||||||
|
Object.entries(registers).forEach(([key, file]) => register(key, file))
|
||||||
|
|
||||||
|
export const i18n = () => init({
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
initialLocale: getLocaleFromNavigator(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export { isLoading, locale, locales }
|
|
@ -0,0 +1,4 @@
|
||||||
|
export default {
|
||||||
|
'en': () => import('./en.json'),
|
||||||
|
'cz': () => import('./cz.json')
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import './app.css'
|
||||||
import App from './App.svelte'
|
import App from './App.svelte'
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
target: document.getElementById('app'),
|
target: document.getElementById('app')
|
||||||
})
|
})
|
||||||
|
|
||||||
export default app
|
export default app
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@600&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@600&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@500&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@500&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import TopImage from '$lib/svg/Top-Image.svelte'
|
import TopImage from '$lib/svg/Top-Image.svelte'
|
||||||
import { Helper } from 'flowbite-svelte'
|
import { Helper } from 'flowbite-svelte'
|
||||||
import { getErrorMessage } from '../utils/authorizationErrors'
|
import { getErrorMessage } from '../utils/authorizationErrors'
|
||||||
|
import Button from '$lib/components/Buttons/Button.svelte'
|
||||||
|
|
||||||
export let purpose = 'login' //possible values login, register
|
export let purpose = 'login' //possible values login, register
|
||||||
|
|
||||||
|
@ -58,13 +59,13 @@
|
||||||
<div class="forgot_password">
|
<div class="forgot_password">
|
||||||
<a href="/forgot-password">Forgot password?</a>
|
<a href="/forgot-password">Forgot password?</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="loginButton" on:click={() => emailLogin()}>
|
<Button class="w-full text-2xl" primary disabled={password.length < 8} on:click={() => emailLogin()}>
|
||||||
{#if state === 'loading'}
|
{#if state === 'loading'}
|
||||||
<Loading class="text-white" />
|
<Loading class="text-white" />
|
||||||
{:else}
|
{:else}
|
||||||
Sign in
|
Log in
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
erantId: params.erantId,
|
erantId: params.erantId,
|
||||||
userName: $user.name,
|
userName: $user.name,
|
||||||
userId: $user.$id,
|
userId: $user.$id,
|
||||||
|
termsAccepted: true,
|
||||||
},
|
},
|
||||||
[Permission.delete(Role.user($user.$id)), Permission.update(Role.user($user.$id)), Permission.read(Role.users('verified'))],
|
[Permission.delete(Role.user($user.$id)), Permission.update(Role.user($user.$id)), Permission.read(Role.users('verified'))],
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,18 +7,19 @@
|
||||||
import { getErrorMessage } from '../utils/authorizationErrors'
|
import { getErrorMessage } from '../utils/authorizationErrors'
|
||||||
import { getUserByErantId } from '$lib/utils/database/users'
|
import { getUserByErantId } from '$lib/utils/database/users'
|
||||||
import { ID } from 'appwrite'
|
import { ID } from 'appwrite'
|
||||||
import { Helper } from 'flowbite-svelte'
|
import { Checkbox, Helper } from 'flowbite-svelte'
|
||||||
import Button from '$lib/components/Buttons/Button.svelte'
|
import Button from '$lib/components/Buttons/Button.svelte'
|
||||||
|
|
||||||
let email = 'dfsafads'
|
let email = ''
|
||||||
let password = 'aaaaaaaaa'
|
let password = ''
|
||||||
let repeatPassword = 'aaaaaaaaa'
|
let repeatPassword = ''
|
||||||
let name = 'fdsafsda'
|
let name = ''
|
||||||
let erantId = 'dfsafdsa'
|
let erantId = ''
|
||||||
|
let termsChecked: boolean = false
|
||||||
|
|
||||||
let state: 'email-sent' | 'register' | 'loading' = 'register'
|
let state: 'email-sent' | 'register' | 'loading' = 'register'
|
||||||
let error: string | null = null
|
let error: string | null = null
|
||||||
$: buttonCodition = name.length > 0 && email.length > 0 && password.length >= 8 && password === repeatPassword && erantId.length > 2
|
$: buttonCodition = name.length > 0 && email.length > 0 && password.length >= 8 && password === repeatPassword && erantId.length > 2 && termsChecked
|
||||||
|
|
||||||
const register = async () => {
|
const register = async () => {
|
||||||
//if (password === repeatPassword || name.length < 8 || email.length < 8) throw new Error('conditions are not fine')
|
//if (password === repeatPassword || name.length < 8 || email.length < 8) throw new Error('conditions are not fine')
|
||||||
|
@ -26,15 +27,18 @@
|
||||||
state = 'loading'
|
state = 'loading'
|
||||||
error = null
|
error = null
|
||||||
if (await getUserByErantId(erantId)) {
|
if (await getUserByErantId(erantId)) {
|
||||||
const err = new Error('fdjsaůl')
|
const err = new Error('Erant ID is already taken')
|
||||||
err['code'] = 1001
|
err['code'] = 1001
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
await account.create(ID.unique(), email, password, name)
|
await account.create(ID.unique(), email, password, name)
|
||||||
await account.createEmailSession(email, password)
|
await account.createEmailSession(email, password)
|
||||||
await account.createVerification(`${location.origin}/register/emailverification/${erantId}`)
|
await account.createVerification(`${location.origin}/register/emailverification/${erantId}`)
|
||||||
state = 'email-sent'
|
state = 'email-sent'
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
|
||||||
error = getErrorMessage(err.code)
|
error = getErrorMessage(err.code)
|
||||||
state = 'register'
|
state = 'register'
|
||||||
}
|
}
|
||||||
|
@ -54,18 +58,20 @@
|
||||||
<Helper class="ml-4" color="red">{error}</Helper>
|
<Helper class="ml-4" color="red">{error}</Helper>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="inform">
|
<div class="inform">
|
||||||
<input bind:value={name} type="text" placeholder="Name" autocomplete="full-name" required />
|
<input bind:value={name} type="text" placeholder="Your name" autocomplete="full-name" required />
|
||||||
<input bind:value={email} type="text" placeholder="E-mail" autocomplete="email" required />
|
<input bind:value={email} type="text" placeholder="Your e-mail" autocomplete="email" required />
|
||||||
<input bind:value={erantId} type="text" placeholder="@your_nickname" autocomplete="email" required />
|
<input bind:value={erantId} type="text" placeholder="@your_nickname" autocomplete="email" required />
|
||||||
<HiddenInput bind:value={password} placeholder="Password" />
|
<HiddenInput bind:value={password} placeholder="Password" />
|
||||||
|
<div class="w-full">
|
||||||
<HiddenInput bind:value={repeatPassword} placeholder="Re-type password" />
|
<HiddenInput bind:value={repeatPassword} placeholder="Re-type password" />
|
||||||
|
{#if password !== repeatPassword && password.length >= 8}
|
||||||
|
<Helper class="flex justify-start w-full pl-4" helperClass="text-sm" color="red">Passwords are not equal</Helper>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input
|
<Checkbox
|
||||||
id="link-checkbox"
|
bind:checked={termsChecked}
|
||||||
type="checkbox"
|
|
||||||
value=""
|
|
||||||
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
|
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<label for="link-checkbox" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
|
<label for="link-checkbox" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
|
||||||
>I agree with the <a href="erant.cz/terms-and-conditions" class="text-blue-600 dark:text-blue-500 hover:underline">terms and conditions</a>,
|
>I agree with the <a href="erant.cz/terms-and-conditions" class="text-blue-600 dark:text-blue-500 hover:underline">terms and conditions</a>,
|
||||||
|
|
|
@ -2,7 +2,7 @@ export const errors = {
|
||||||
1001: 'Nickname is already taken',
|
1001: 'Nickname is already taken',
|
||||||
409: 'A user with the same email already is in Erant',
|
409: 'A user with the same email already is in Erant',
|
||||||
401: 'Invalid credentials.',
|
401: 'Invalid credentials.',
|
||||||
400: 'Password must be at least 8 characters.',
|
400: 'Email musts be valid.',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getErrorMessage = (code: number) => {
|
export const getErrorMessage = (code: number) => {
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import Overlay from '../lib/components/Layouts/LayoutImg.svelte'
|
import Overlay from '$lib/components/Layouts/LayoutImg.svelte'
|
||||||
import Section from '../lib/components/Common/Section.svelte'
|
import Section from '$lib/components/Common/Section.svelte'
|
||||||
import Button from '../lib/components/Buttons/Button.svelte'
|
import Button from '$lib/components/Buttons/Button.svelte'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<Overlay img="/assets/images/main.jpg">
|
<Overlay img="/assets/images/main.jpg">
|
||||||
<div>
|
<div>
|
||||||
<span>Erant</span>
|
<span>Erant</span>
|
||||||
|
|
|
@ -2,24 +2,24 @@
|
||||||
import NavigationBarLayout from '../../lib/components/Layouts/NavigationBarLayout.svelte'
|
import NavigationBarLayout from '../../lib/components/Layouts/NavigationBarLayout.svelte'
|
||||||
import Result from './Components/Result.svelte'
|
import Result from './Components/Result.svelte'
|
||||||
import Top from './Components/Top.svelte'
|
import Top from './Components/Top.svelte'
|
||||||
import { getExpiriences } from '$lib/utils/database/game'
|
import { getExperiences } from '$lib/utils/database/experience'
|
||||||
import Loading from '$lib/components/Common/Loading.svelte'
|
import Loading from '$lib/components/Common/Loading.svelte'
|
||||||
|
import Comparment from '../homepage/Components/Comparment.svelte'
|
||||||
|
|
||||||
let Search: string
|
let Search: string
|
||||||
|
//
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<Top bind:search_value={Search} />
|
<Top bind:search_value={Search} />
|
||||||
|
|
||||||
<div class="results">
|
<div class="results">
|
||||||
{#await getExpiriences()}
|
{#await getExperiences()}
|
||||||
<div class="w-full h-24 flex items-center justify-center">
|
<div class="w-full h-24 flex items-center justify-center">
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
{:then expiriences}
|
{:then expiriences}
|
||||||
{#each expiriences as result}
|
<Comparment direction="col" items={expiriences} />
|
||||||
<Result name={result.name} location={result.location} link={result.link} />
|
|
||||||
{/each}
|
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
<div class="end" />
|
<div class="end" />
|
||||||
|
|
|
@ -1,54 +1,63 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Section from '../../../lib/components/Common/Section.svelte'
|
import Section from '$lib/components/Common/Section.svelte'
|
||||||
import Button from '../../../lib/components/Buttons/Button.svelte'
|
import Button from '$lib/components/Buttons/Button.svelte'
|
||||||
import Image from '../../../lib/components/Common/Image.svelte'
|
import Image from '$lib/components/Common/Image.svelte'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import Loading from '$lib/components/Common/Loading.svelte'
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let control: 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
export let control: 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
||||||
export let nextQuestion
|
export let imgSrc: string = ''
|
||||||
export let imgSrc
|
export let loading: boolean
|
||||||
|
//
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="wrap">
|
<div class="wrap mt-2">
|
||||||
<div class="h-[var(--quizHeader)] w-full flex relative top-0 justify-center items-center">
|
<div class="w-full flex relative top-0 justify-center items-center">
|
||||||
<div class="flex justify-center items-center flex-col flex-wrap gap-3">
|
<div class="flex w-full justify-center items-center flex-col flex-wrap gap-3">
|
||||||
<span class="title"><slot name="title" /></span>
|
<span class="title h-min"><slot name="title" /></span>
|
||||||
|
{#if imgSrc}
|
||||||
<span style="width: 100%"><Image class="w-full h-[200px]" src={imgSrc} /></span>
|
<span style="width: 100%"><Image class="w-full h-[200px]" src={imgSrc} /></span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Section style="height: calc(100% - var(--quizHeader)); position: relative">
|
<Section style="height: calc(100% - var(--quizHeader)); position: relative">
|
||||||
<div class="flex items-center justify-center flex-col w-full gap-24 p-4">
|
<div class="flex items-center justify-center flex-col w-full gap-24 p-4">
|
||||||
<div class="popis">
|
<div class="popis">
|
||||||
<span> Popis úkolu: </span>
|
|
||||||
<span> <slot name="about" /></span>
|
<span> <slot name="about" /></span>
|
||||||
</div>
|
</div>
|
||||||
{#if control === 'wrong-firstTime' || control === 'wrong-secondTime'}
|
|
||||||
<div class="popis">
|
|
||||||
<span> Nápověda: </span>
|
|
||||||
<span> <slot name="hint" /></span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="flex items-center justify-center flex-col w-full gap-6">
|
<div class="flex items-center justify-center flex-col w-full gap-6">
|
||||||
<span class="relative"> <slot name="answers" /> </span>
|
<span class="relative"> <slot name="answers" /> </span>
|
||||||
{#if control === 'not-control' || control === 'correct' || control === 'wrong-secondTime'}
|
<div class="w-full relative flex justify-center h-fit size">
|
||||||
<Button on:submit={nextQuestion} primary class="w-3/4 max-w-sm min-w-[400px] h-16 bottom-0 m-10 relative">Na další otázku</Button>
|
|
||||||
{:else if control === 'wrong-firstTime' || control === null}
|
|
||||||
<Button on:submit primary class="w-3/4 max-w-sm min-w-[400px] h-16 bottom-0 m-10 relative">Vyhodnotit</Button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full relative bottom-[170px] flex justify-center h-fit size">
|
|
||||||
{#if control === 'not-control' || control === null}
|
{#if control === 'not-control' || control === null}
|
||||||
<span />
|
<span />
|
||||||
{:else if control === 'correct'}
|
{:else if control === 'wrong-secondTime' || control === 'correct'}
|
||||||
<span style="color:greenyellow">správně</span>
|
<div class="flex w-auto h-auto flex-wrap flex-col items-center gap-2">
|
||||||
|
<span class={`${control === 'correct' ? 'text-green-500' : 'text-red-500'}`}>{control === 'correct' ? 'správně' : 'špatně'}</span>
|
||||||
|
<span> <slot name="after" /></span>
|
||||||
|
</div>
|
||||||
{:else if control === 'wrong-firstTime'}
|
{:else if control === 'wrong-firstTime'}
|
||||||
<span style="color:red">druhypokus</span>
|
<div class="flex w-auto h-auto flex-wrap flex-col items-center gap-2">
|
||||||
{:else if control === 'wrong-secondTime'}
|
<span class="text-red-500">To není správně, zkus to znovu</span>
|
||||||
<span style="color:red">špatně</span>
|
<span> <slot name="hint" /></span>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{#if control === 'not-control' || control === 'correct' || control === 'wrong-secondTime'}
|
||||||
|
<Button on:submit={() => dispatch('nextQuestion')} primary class="w-3/4 max-w-sm min-w-[400px] h-16 bottom-0 m-10 relative">Na další otázku</Button>
|
||||||
|
{:else if control === 'wrong-firstTime' || control === null}
|
||||||
|
<Button disabled={loading} on:submit primary class="w-3/4 max-w-sm min-w-[400px] h-16 bottom-0 m-10 relative">
|
||||||
|
{#if loading}
|
||||||
|
<Loading />
|
||||||
|
{:else}
|
||||||
|
Vyhodnotit
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,28 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import LayoutImg from '../../../lib/components/Layouts/LayoutImg.svelte'
|
import LayoutImg from '../../../lib/components/Layouts/LayoutImg.svelte'
|
||||||
import Button from '../../../lib/components/Buttons/Button.svelte'
|
import Button from '../../../lib/components/Buttons/Button.svelte'
|
||||||
export let img
|
import { Experience } from '$lib/TStypes/experiences'
|
||||||
|
import { navigate } from '$lib/router'
|
||||||
|
export let gameData: Experience
|
||||||
|
export let client
|
||||||
|
let score = (client.points / client.possiblePointsToSeize) * 100
|
||||||
|
console.log(score)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LayoutImg {img}>
|
<LayoutImg img={gameData.ExpImage}>
|
||||||
<div class="w-full h-full flex flex-wrap flex-row gap-4 justify-center">
|
<div class="w-full h-auto flex flex-wrap flex-row gap-4 justify-center">
|
||||||
<div class="h-full w-full flex justify-self-center justify-center text-[32px]"><slot /></div>
|
<div class="h-full w-full flex justify-self-center justify-center text-[32px] flex-wrap flex-col gap-4 items-center">
|
||||||
<Button class="w-80 absolute bottom-0 mb-6" href="/">ukončit hru</Button>
|
<span> Získali jste {client.points} / {client.possiblePointsToSeize} bodů</span>
|
||||||
|
<span>
|
||||||
|
{#if score > 90}
|
||||||
|
{@html gameData.ExpEnd100}
|
||||||
|
{:else if score > 50}
|
||||||
|
{@html gameData.ExpEnd60}
|
||||||
|
{:else}
|
||||||
|
{@html gameData.ExpEnd0}
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Button class="w-80 mt-8 " on:click={() => navigate(-1)}>ukončit hru</Button>
|
||||||
</div>
|
</div>
|
||||||
</LayoutImg>
|
</LayoutImg>
|
||||||
|
|
|
@ -1,23 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Layout from '../Components/Layout.svelte'
|
|
||||||
import { CheckPoint } from '$lib/TStypes/experiences'
|
|
||||||
|
|
||||||
export let data: { name: string; checkPoint: CheckPoint }
|
|
||||||
const { checkPoint, name } = data
|
|
||||||
|
|
||||||
export let nextQuestion
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout
|
<div />
|
||||||
imgSrc={'gameData.question.thumbnail'}
|
|
||||||
control={'not-control'}
|
|
||||||
nextQuestion={() => {
|
|
||||||
nextQuestion()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span slot="title">{name}</span>
|
|
||||||
<span slot="about">{@html checkPoint.CPText}</span>
|
|
||||||
</Layout>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -2,45 +2,22 @@
|
||||||
import Layout from '../Components/Layout.svelte'
|
import Layout from '../Components/Layout.svelte'
|
||||||
import CheckBox from '../../../lib/components/Inputs/Checkbox.svelte'
|
import CheckBox from '../../../lib/components/Inputs/Checkbox.svelte'
|
||||||
import { CheckPoint } from '$lib/TStypes/experiences'
|
import { CheckPoint } from '$lib/TStypes/experiences'
|
||||||
import { answer } from '$lib/utils/database/game'
|
import { answer } from '$lib/utils/database/experience'
|
||||||
|
|
||||||
|
export let checkPoint: CheckPoint
|
||||||
|
export let myAnswer
|
||||||
|
|
||||||
|
export let clear: false | true = false
|
||||||
|
$: if (clear) myAnswers = new Array(checkPoint.CPOptions.length).fill(false)
|
||||||
|
|
||||||
export let data: { name: string; checkPoint: CheckPoint }
|
|
||||||
const { checkPoint, name } = data
|
|
||||||
let myAnswers = new Array(checkPoint.CPOptions.length).fill(false)
|
let myAnswers = new Array(checkPoint.CPOptions.length).fill(false)
|
||||||
|
$: myAnswer = checkPoint.CPOptions.filter((item, i) => {
|
||||||
export let control: 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
|
||||||
export let nextQuestion
|
|
||||||
export let attempt: 1 | 2 = 1
|
|
||||||
|
|
||||||
const answerCheckBox = async () => {
|
|
||||||
const arr = checkPoint.CPOptions.filter((item, i) => {
|
|
||||||
if (myAnswers[i] === true) return item
|
if (myAnswers[i] === true) return item
|
||||||
})
|
})
|
||||||
const res = await answer(checkPoint.$id, arr)
|
|
||||||
if (res) control = 'correct'
|
|
||||||
else if (attempt === 1) control = 'wrong-firstTime'
|
|
||||||
else control = 'wrong-secondTime'
|
|
||||||
attempt++
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout
|
|
||||||
imgSrc={'gameData.question.thumbnail'}
|
|
||||||
nextQuestion={() => {
|
|
||||||
nextQuestion()
|
|
||||||
myAnswers = new Array(checkPoint.CPOptions.length).fill(false)
|
|
||||||
}}
|
|
||||||
{control}
|
|
||||||
on:submit={async () => answerCheckBox()}
|
|
||||||
>
|
|
||||||
<span slot="title">{name}</span>
|
|
||||||
<span slot="about">{@html checkPoint.CPText}</span>
|
|
||||||
<span slot="hint"> {@html checkPoint.CPHint} </span>
|
|
||||||
<div slot="answers">
|
|
||||||
{#each checkPoint.CPOptions as label, i}
|
{#each checkPoint.CPOptions as label, i}
|
||||||
<span class="self-baseline">
|
<span class="self-baseline">
|
||||||
<CheckBox bind:checked={myAnswers[i]}>{label}</CheckBox>
|
<CheckBox bind:checked={myAnswers[i]}>{label}</CheckBox>
|
||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
|
|
|
@ -1,47 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Layout from '../Components/Layout.svelte'
|
|
||||||
import Input from '../../../lib/components/Inputs/Input.svelte'
|
import Input from '../../../lib/components/Inputs/Input.svelte'
|
||||||
import { CheckPoint } from '$lib/TStypes/experiences'
|
import { CheckPoint } from '$lib/TStypes/experiences'
|
||||||
import { answer } from '$lib/utils/database/game'
|
import { answer } from '$lib/utils/database/experience'
|
||||||
|
|
||||||
export let data: { name: string; checkPoint: CheckPoint }
|
export let myAnswer = ''
|
||||||
const { checkPoint, name } = data
|
export let clear: false | true = false
|
||||||
let myAnswer = ''
|
$: if (clear) myAnswer = ''
|
||||||
|
|
||||||
export let control: 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
|
||||||
export let nextQuestion
|
|
||||||
export let attempt: 1 | 2 = 1
|
|
||||||
|
|
||||||
const answer_ = async () => {
|
|
||||||
const res = await answer(checkPoint.$id, myAnswer)
|
|
||||||
if (res) control = 'correct'
|
|
||||||
else if (attempt === 1) control = 'wrong-firstTime'
|
|
||||||
else control = 'wrong-secondTime'
|
|
||||||
attempt++
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout
|
|
||||||
imgSrc={'gameData.question.thumbnail'}
|
|
||||||
nextQuestion={() => {
|
|
||||||
nextQuestion()
|
|
||||||
myAnswer = ''
|
|
||||||
}}
|
|
||||||
{control}
|
|
||||||
on:submit={() => (myAnswer !== '' ? answer_() : null)}
|
|
||||||
>
|
|
||||||
<span slot="title">{name}</span>
|
|
||||||
<span slot="about">{@html checkPoint.CPText}</span>
|
|
||||||
<span slot="hint"> {@html checkPoint.CPHint} </span>
|
|
||||||
<div slot="answers">
|
|
||||||
<span class="self-baseline">
|
|
||||||
<Input type="number" bind:value={myAnswer} class="w-full min-w-[400px] max-w-[500px] h-12" />
|
<Input type="number" bind:value={myAnswer} class="w-full min-w-[400px] max-w-[500px] h-12" />
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
/*.input::-webkit-inner-spin-button {
|
|
||||||
background: blue;
|
|
||||||
}*/
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import parseQuestion from '../../../lib/utils/parseQuestion'
|
|
||||||
import Marker from '../../../lib/components/Map/Marker.svelte'
|
import Marker from '../../../lib/components/Map/Marker.svelte'
|
||||||
import TextForm from './TextForm.svelte'
|
import TextForm from './TextForm.svelte'
|
||||||
import NumberForm from './NumberForm.svelte'
|
import NumberForm from './NumberForm.svelte'
|
||||||
|
@ -8,9 +7,17 @@
|
||||||
import IntervalForm from './IntervalForm.svelte'
|
import IntervalForm from './IntervalForm.svelte'
|
||||||
import QrCode from './QrCode.svelte'
|
import QrCode from './QrCode.svelte'
|
||||||
import Finish from './Finish.svelte'
|
import Finish from './Finish.svelte'
|
||||||
import { data } from '$lib/stores/game'
|
import { data } from '$lib/stores/stores'
|
||||||
import Erantmap from '$lib/components/Map/Erantmap.svelte'
|
import Erantmap from '$lib/components/Map/Erantmap.svelte'
|
||||||
import Info from './Info.svelte'
|
import Info from './Info.svelte'
|
||||||
|
import { Experience } from '$lib/TStypes/experiences'
|
||||||
|
import Layout from '../Components/Layout.svelte'
|
||||||
|
import { answer, AnswerState, getUserAnswer, getUserProgressAsStore } from '$lib/utils/database/experience'
|
||||||
|
import { user } from '$lib/appwrite'
|
||||||
|
import Button from '$lib/components/Buttons/Button.svelte'
|
||||||
|
import LayoutImg from '$lib/components/Layouts/LayoutImg.svelte'
|
||||||
|
import { Models } from 'appwrite'
|
||||||
|
import { Writable } from 'svelte/store'
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
TEXT: TextForm,
|
TEXT: TextForm,
|
||||||
|
@ -22,80 +29,127 @@
|
||||||
INFO: Info,
|
INFO: Info,
|
||||||
}
|
}
|
||||||
|
|
||||||
export let control: 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null = null
|
export let control: AnswerState = null
|
||||||
let view: 'question' | 'map' | 'end' = 'map'
|
let view: 'question' | 'map' | 'end' | 'start' | 'start-map' = 'start-map'
|
||||||
|
export let gameData: Experience //data
|
||||||
|
$: [userProgress, userProgressLoading] = gameData ? getUserProgressAsStore(gameData?.$id) : []
|
||||||
|
|
||||||
export let gameData: any = {} //data
|
let client = {
|
||||||
$: console.log(gameData)
|
|
||||||
|
|
||||||
let clientAnswers = {
|
|
||||||
//user data about game
|
//user data about game
|
||||||
pos: 0,
|
pos: 0,
|
||||||
end: gameData.checkPoints.length - 1, //kolik otázek
|
end: gameData.ExpCPsID.length - 1, //kolik otázek
|
||||||
points: 0, //body
|
points: 0, //body
|
||||||
|
possiblePointsToSeize: gameData.checkPoints.length * 2,
|
||||||
|
}
|
||||||
|
$: if (client.pos < $userProgress?.length - 1 + 1) {
|
||||||
|
// nastaví na continue
|
||||||
|
client.pos = $userProgress?.length - 1 + 1
|
||||||
|
view = 'map'
|
||||||
}
|
}
|
||||||
|
|
||||||
$: console.log(clientAnswers.pos === clientAnswers.end + 1)
|
$: if (gameData.checkPoints[client.pos].CPType === 'INFO' && !$userProgressLoading) control = 'not-control'
|
||||||
|
|
||||||
$: if (control === 'correct') clientAnswers.points += 2 //body bodování
|
$: console.log(control)
|
||||||
|
|
||||||
const nextQuestion = () => {
|
const nextQuestion = () => {
|
||||||
//další otázka
|
//další otázka
|
||||||
|
|
||||||
console.log({ control })
|
|
||||||
|
|
||||||
control = null
|
control = null
|
||||||
if (clientAnswers.pos === clientAnswers.end) view = 'end'
|
if (client.pos === client.end) view = 'end'
|
||||||
else {
|
else {
|
||||||
clientAnswers.pos++
|
client.pos++
|
||||||
view = 'map'
|
view = 'map'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//let answers
|
$: checkPoint = gameData.checkPoints[client.pos]
|
||||||
//$: if (clientAnswers.pos < clientAnswers.end) answers = parseQuestion(gameData.questions[clientAnswers.pos].answer, gameData.questions[clientAnswers.pos].type) //delete
|
$: checkPointType = checkPoint.CPType
|
||||||
|
|
||||||
let page = null
|
let page = null
|
||||||
$: page = view === 'question' ? components[gameData.checkPoints[clientAnswers.pos].CPType] : null
|
$: page = view === 'question' ? components[checkPointType] : null
|
||||||
|
|
||||||
let [lat, lng] = [null, null]
|
let [lat, lng] = [null, null]
|
||||||
$: if (clientAnswers.pos < clientAnswers.end) [lat, lng] = $data.checkPoints[clientAnswers.pos].CPLocation
|
$: if (client.pos < client.end) [lat, lng] = view === 'map' ? gameData.checkPoints[client.pos].CPLocation : gameData.ExpLocation
|
||||||
|
|
||||||
let user = { lat: null, lng: null }
|
let userLocation = { lat: 0, lng: 0 }
|
||||||
|
|
||||||
$: console.log(gameData.checkPoints[clientAnswers.pos].CPType, clientAnswers.pos, view)
|
let myAnswer: string | string[]
|
||||||
|
let clear: boolean = false
|
||||||
|
let answerLoading = false
|
||||||
|
|
||||||
/* //set user to localstorage
|
const checkAnswer = async () => {
|
||||||
$: if (clientAnswers.pos !== clientAnswers.end && clientAnswers.pos !== 0) {
|
answerLoading = true
|
||||||
//nastaví
|
try {
|
||||||
localStorage.setItem(`/${gameData.url}`, JSON.stringify(clientAnswers.pos))
|
const res = await answer(gameData.$id, checkPoint.$id, myAnswer)
|
||||||
localStorage.setItem('lastGame', `/${gameData.url}`)
|
if (checkPointType === 'INFO') {
|
||||||
|
nextQuestion()
|
||||||
} else {
|
} else {
|
||||||
//vymaže když jsi dohrál
|
if (res) {
|
||||||
localStorage.removeItem(`/${gameData.url}`)
|
control = 'correct'
|
||||||
localStorage.removeItem('lastGame')
|
client.points += 2
|
||||||
|
} else if (control === null) {
|
||||||
|
control = 'wrong-firstTime'
|
||||||
|
clear = true
|
||||||
|
} else control = 'wrong-secondTime'
|
||||||
|
setTimeout(() => (clear = false), 400)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
answerLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
//is user already in game
|
|
||||||
const userInGame = JSON.parse(localStorage.getItem(location.pathname))
|
|
||||||
if (userInGame) {
|
|
||||||
clientAnswers.pos = userInGame
|
|
||||||
}*/
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input type="number" bind:value={clientAnswers.pos} />
|
<input type="number" bind:value={client.pos} />
|
||||||
<button on:click={() => (view = 'question')}>disappear map</button>
|
<button on:click={() => (view = view === 'start-map' ? 'start' : 'question')}>disappear map</button>
|
||||||
|
|
||||||
{#if view === 'map'}
|
{#if view === 'map' || view === 'start-map'}
|
||||||
<Erantmap bind:user class="w-full h-full">
|
<Erantmap bind:userLocation class="w-full h-full">
|
||||||
<Marker on:enter={() => (view = 'question')} {lat} {lng} {user} />
|
<Marker on:enter={() => (view = view === 'start-map' ? 'start' : 'question')} {lat} {lng} {userLocation} />
|
||||||
</Erantmap>
|
</Erantmap>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if view === 'start'}
|
||||||
|
<LayoutImg img={gameData.ExpImage}>
|
||||||
|
<div class="w-full h-auto flex flex-wrap flex-row gap-4 justify-center">
|
||||||
|
<div class="h-full w-full flex justify-self-center justify-center text-[32px] flex-wrap flex-col gap-4 items-center">
|
||||||
|
{@html gameData.ExpStart}
|
||||||
|
</div>
|
||||||
|
<Button class="w-80 mt-8 " on:click={() => (view = 'map')}>pokračovat</Button>
|
||||||
|
</div>
|
||||||
|
</LayoutImg>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if view === 'question'}
|
{#if view === 'question'}
|
||||||
<svelte:component this={page} data={{ checkPoint: gameData.checkPoints[clientAnswers.pos], name: gameData.ExpName }} {nextQuestion} bind:control />
|
<Layout
|
||||||
|
loading={answerLoading}
|
||||||
|
imgSrc={checkPoint.CPImage}
|
||||||
|
on:submit={() => checkAnswer()}
|
||||||
|
on:nextQuestion={() => {
|
||||||
|
if (checkPointType === 'INFO') checkAnswer()
|
||||||
|
else nextQuestion()
|
||||||
|
}}
|
||||||
|
{control}
|
||||||
|
>
|
||||||
|
<span slot="title">{checkPoint.CPName}</span>
|
||||||
|
<span slot="about">{@html checkPoint.CPText}</span>
|
||||||
|
|
||||||
|
<span slot="hint">
|
||||||
|
{#if checkPoint.CPHint}
|
||||||
|
{@html checkPoint.CPHint}
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
<span slot="after">
|
||||||
|
{#if checkPoint.CPAfter}
|
||||||
|
{@html checkPoint.CPAfter}
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
<div slot="answers">
|
||||||
|
<span class="self-baseline">
|
||||||
|
<svelte:component this={page} {clear} {checkPoint} bind:myAnswer />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
{#if view === 'end'}
|
{#if view === 'end'}
|
||||||
<Finish img={gameData.thumbnail}>
|
<Finish {client} {gameData} />
|
||||||
<span> Získali jste {clientAnswers.points} / {clientAnswers.end * 2} bodů</span>
|
|
||||||
</Finish>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -2,44 +2,17 @@
|
||||||
import Radio from '../../../lib/components/Inputs/Radio.svelte'
|
import Radio from '../../../lib/components/Inputs/Radio.svelte'
|
||||||
import Layout from '../Components/Layout.svelte'
|
import Layout from '../Components/Layout.svelte'
|
||||||
import { CheckPoint } from '$lib/TStypes/experiences'
|
import { CheckPoint } from '$lib/TStypes/experiences'
|
||||||
import { answer } from '$lib/utils/database/game'
|
import { answer } from '$lib/utils/database/experience'
|
||||||
|
|
||||||
export let data: { name: string; checkPoint: CheckPoint }
|
export let checkPoint: CheckPoint
|
||||||
const { checkPoint, name } = data
|
export let myAnswer = ''
|
||||||
let myAnswer = ''
|
|
||||||
|
|
||||||
export let control: 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
export let clear: false | true = false
|
||||||
export let nextQuestion
|
$: if (clear) myAnswer = ''
|
||||||
export let attempt: 1 | 2 = 1
|
|
||||||
|
|
||||||
const answer_ = async () => {
|
|
||||||
const res = await answer(checkPoint.$id, myAnswer)
|
|
||||||
if (res) control = 'correct'
|
|
||||||
else if (attempt === 1) control = 'wrong-firstTime'
|
|
||||||
else control = 'wrong-secondTime'
|
|
||||||
attempt++
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout
|
|
||||||
imgSrc={'gameData.question.thumbnail'}
|
|
||||||
{control}
|
|
||||||
nextQuestion={() => {
|
|
||||||
nextQuestion()
|
|
||||||
myAnswer = ''
|
|
||||||
}}
|
|
||||||
on:submit={() => (myAnswer !== '' ? answer_() : null)}
|
|
||||||
>
|
|
||||||
<span slot="title">{name}</span>
|
|
||||||
<span slot="about">{@html checkPoint.CPText}</span>
|
|
||||||
<div slot="answers">
|
|
||||||
{#each checkPoint.CPOptions as label}
|
{#each checkPoint.CPOptions as label}
|
||||||
<span class="self-baseline">
|
<span class="self-baseline">
|
||||||
<Radio value={label} bind:group={myAnswer}>{label}</Radio>
|
<Radio value={label} bind:group={myAnswer}>{label}</Radio>
|
||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -2,43 +2,11 @@
|
||||||
import Layout from '../Components/Layout.svelte'
|
import Layout from '../Components/Layout.svelte'
|
||||||
import Input from '../../../lib/components/Inputs/Input.svelte'
|
import Input from '../../../lib/components/Inputs/Input.svelte'
|
||||||
import { CheckPoint } from '$lib/TStypes/experiences'
|
import { CheckPoint } from '$lib/TStypes/experiences'
|
||||||
import { answer } from '$lib/utils/database/game'
|
import { answer } from '$lib/utils/database/experience'
|
||||||
|
|
||||||
export let data: { name: string; checkPoint: CheckPoint }
|
export let myAnswer = ''
|
||||||
const { checkPoint, name } = data
|
export let clear: false | true = false
|
||||||
let myAnswer = ''
|
$: if (clear) myAnswer = ''
|
||||||
|
|
||||||
export let control: 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
|
||||||
export let nextQuestion
|
|
||||||
export let attempt: 1 | 2 = 1
|
|
||||||
|
|
||||||
const answer_ = async () => {
|
|
||||||
const res = await answer(checkPoint.$id, myAnswer)
|
|
||||||
if (res) control = 'correct'
|
|
||||||
else if (attempt === 1) control = 'wrong-firstTime'
|
|
||||||
else control = 'wrong-secondTime'
|
|
||||||
attempt++
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout
|
|
||||||
imgSrc={'gameData.question.thumbnail'}
|
|
||||||
{control}
|
|
||||||
nextQuestion={() => {
|
|
||||||
nextQuestion()
|
|
||||||
myAnswer = ''
|
|
||||||
}}
|
|
||||||
on:submit={() => (myAnswer !== '' ? answer_() : null)}
|
|
||||||
>
|
|
||||||
<span slot="title">{name}</span>
|
|
||||||
<span slot="about">{@html checkPoint.CPText}</span>
|
|
||||||
<span slot="hint"> {@html checkPoint.CPHint} </span>
|
|
||||||
<div slot="answers">
|
|
||||||
<span class="self-baseline">
|
|
||||||
<Input type="text" bind:value={myAnswer} class="w-full min-w-[400px] max-w-[500px] h-12" />
|
<Input type="text" bind:value={myAnswer} class="w-full min-w-[400px] max-w-[500px] h-12" />
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -6,74 +6,72 @@
|
||||||
import IconStar from '../../lib/svg/Star.svelte'
|
import IconStar from '../../lib/svg/Star.svelte'
|
||||||
import IconPoint from '../../lib/svg/Point.svelte'
|
import IconPoint from '../../lib/svg/Point.svelte'
|
||||||
import Loading from '../../lib/components/Common/Loading.svelte'
|
import Loading from '../../lib/components/Common/Loading.svelte'
|
||||||
import { data } from '../../lib/stores/game'
|
import { data } from '../../lib/stores/stores'
|
||||||
import GeolocateControl from '@beyonk/svelte-mapbox/src/lib/map/controls/GeolocateControl.svelte'
|
import GeolocateControl from '@beyonk/svelte-mapbox/src/lib/map/controls/GeolocateControl.svelte'
|
||||||
import Map from '../../lib/components/Map/Map.svelte'
|
import Map from '../../lib/components/Map/Map.svelte'
|
||||||
import Renderer from './Forms/Renderer.svelte'
|
import Renderer from './Forms/Renderer.svelte'
|
||||||
import Redirect from '../../lib/components/Common/Redirect.svelte'
|
|
||||||
import ImageSlider from '../../lib/components/Common/ImageSlider.svelte'
|
|
||||||
import Marker from '../../lib/components/Map/Marker.svelte'
|
import Marker from '../../lib/components/Map/Marker.svelte'
|
||||||
import collections from '$lib/collections'
|
|
||||||
import { Query } from 'appwrite'
|
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { load } from '$lib/utils/database/game'
|
import { getUserProgressAsStore, load } from '$lib/utils/database/experience'
|
||||||
import { getLocationDataFromLatAndLong } from '$lib/utils/locations'
|
import { getLocationDataFromLatAndLong } from '$lib/utils/locations'
|
||||||
import { navigate } from '$lib/router'
|
import { navigate } from '$lib/router'
|
||||||
|
import { Experience } from '$lib/TStypes/experiences'
|
||||||
|
import Progressbar from '$lib/components/erant/Progressbar.svelte'
|
||||||
|
|
||||||
export let params: { gameurl: string }
|
export let params: { gameurl: string }
|
||||||
|
let gameData: Experience //
|
||||||
|
$: [userProgress] = gameData ? getUserProgressAsStore(gameData?.$id) : []
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
$data = await load(params.gameurl, 5, (preview) => {
|
gameData = await load(params.gameurl, 2, (preview) => {
|
||||||
$data = preview
|
gameData = preview
|
||||||
view = 'game-preview'
|
view = 'experience-preview'
|
||||||
})
|
})
|
||||||
view = 'game-preview'
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
navigate('/error')
|
navigate('/error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$: console.log($data)
|
|
||||||
|
|
||||||
$: assets = $data?.questions
|
/*$: assets = gameData?.questions
|
||||||
?.filter((q) => q.thumbnail !== null)
|
?.filter((q) => q.thumbnail !== null)
|
||||||
?.slice(0, 8)
|
?.slice(0, 8)
|
||||||
?.map((q) => q.thumbnail)
|
?.map((q) => q.thumbnail)*/
|
||||||
|
|
||||||
let view: 'game-play' | 'game-loading' | 'game-preview' = 'game-loading'
|
let view: 'experience-play' | 'experience-loading' | 'experience-preview' = 'experience-loading'
|
||||||
|
|
||||||
//is user already in game
|
//is user already in game
|
||||||
//const userInGame = JSON.parse(localStorage.getItem(location.pathname))
|
//const userInGame = JSON.parse(localStorage.getItem(location.pathname))
|
||||||
//$: if ($data && userInGame) view = 'game-play'
|
//$: if (gameData && userInGame) view = 'game-play'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if view === 'game-loading'}
|
{#if view === 'experience-loading'}
|
||||||
<h1 class="flex items-center justify-center flex-col">
|
<h1 class="flex items-center justify-center flex-col">
|
||||||
<span>Experience is loading...</span>
|
<span>Experience is loading...</span>
|
||||||
<Loading />
|
<Loading />
|
||||||
</h1>
|
</h1>
|
||||||
{:else if view === 'game-preview'}
|
{:else if view === 'experience-preview'}
|
||||||
<Overlay shareData={{ url: window.location.href }} img={$data.ExpImage}>
|
<Overlay shareData={{ url: window.location.href }} img={gameData.ExpImage}>
|
||||||
<div>
|
<div>
|
||||||
<span class="text-[36px]">{$data.ExpName}</span>
|
<span class="text-[36px]">{gameData.ExpName}</span>
|
||||||
<div class="bubbles">
|
<div class="bubbles">
|
||||||
<Bubble background="blue">
|
<Bubble background="blue">
|
||||||
<span slot="icon"><IconStar /></span>
|
<span slot="icon"><IconStar /></span>
|
||||||
<span> {$data.rating} </span>
|
<span> {gameData.rating} </span>
|
||||||
</Bubble>
|
</Bubble>
|
||||||
<Bubble background="white">
|
<Bubble background="white">
|
||||||
<span slot="icon"><IconPoint /></span>
|
<span slot="icon"><IconPoint /></span>
|
||||||
{#await getLocationDataFromLatAndLong($data.ExpLocation[0], $data.ExpLocation[1]) then { city }}
|
{#await getLocationDataFromLatAndLong(gameData.ExpLocation[0], gameData.ExpLocation[1]) then { city }}
|
||||||
<span>{city}</span>
|
<span>{city}</span>
|
||||||
{/await}
|
{/await}
|
||||||
</Bubble>
|
</Bubble>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $data.ExpIntroduction}
|
{#if gameData.ExpIntroduction}
|
||||||
<Section title="Popis">
|
<Section title="Popis">
|
||||||
<span>
|
<span>
|
||||||
{@html $data.ExpIntroduction}
|
{@html gameData.ExpIntroduction}
|
||||||
</span>
|
</span>
|
||||||
</Section>
|
</Section>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -85,23 +83,26 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
<Section title="Progress">
|
||||||
|
<Progressbar max={gameData.ExpCPsID.length} progress={$userProgress.length} showWrittenProgress />
|
||||||
|
</Section>
|
||||||
|
|
||||||
<div class="w-full relative">
|
<div class="w-full relative">
|
||||||
<div class="px-4 m-auto" style="max-width: var(--max-viewport-width);">
|
<div class="px-4 m-auto" style="max-width: var(--max-viewport-width);">
|
||||||
<Map radius class="w-full h-44" center={{ lng: $data.ExpLocation[1], lat: $data.ExpLocation[0] }}>
|
<Map radius class="w-full h-44" center={{ lng: gameData.ExpLocation[1], lat: gameData.ExpLocation[0] }}>
|
||||||
{#each $data.checkPoints as { CPLocation: [lat, lng] }}
|
{#each gameData.checkPoints as { CPLocation: [lat, lng] }}
|
||||||
<Marker {lat} {lng} />
|
<Marker {lat} {lng} />
|
||||||
{/each}
|
{/each}
|
||||||
</Map>
|
</Map>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $data.checkPoints.length}
|
{#if gameData.checkPoints.length}
|
||||||
<Button on:click={() => (view = 'game-play')} primary>Hrát</Button>
|
<Button on:click={() => (view = 'experience-play')} primary>{$userProgress ? 'Pokračovat' : 'Hrát'}</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
{:else if view === 'game-play'}
|
{:else if view === 'experience-play'}
|
||||||
<Renderer gameData={$data} />
|
<Renderer {gameData} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
|
@ -1,19 +1,18 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { navigate } from '$lib/router'
|
import { navigate } from '$lib/router'
|
||||||
import CompartmentItem from './Compartment_Item.svelte'
|
import CompartmentItem from './Compartment_Item.svelte'
|
||||||
import Headline from './Headline.svelte'
|
import Headline from './Headline.svelte'
|
||||||
|
|
||||||
export let items = []
|
export let items = []
|
||||||
|
export let direction: 'col' | 'row' = 'row'
|
||||||
console.log(items)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<Headline content="Inspiration on your trip" />
|
<Headline content="Inspiration on your trip" />
|
||||||
|
|
||||||
<div class="options">
|
<div class={`options ${direction === 'row' ? 'flex-row' : 'flex-col'}`}>
|
||||||
{#each items as item}
|
{#each items as item}
|
||||||
<CompartmentItem on:click={() => navigate(`${item.ExpURL}`)} price={item.price} location={item.location} name={item.ExpName} image={item.ExpImage} />
|
<CompartmentItem on:click={() => navigate(`${item.ExpURL}`)} city={item.city} name={item.ExpName} image={item.ExpImage} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,7 +35,6 @@
|
||||||
|
|
||||||
.section > .options {
|
.section > .options {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
export let location = 'Home'
|
export let city = 'Home'
|
||||||
export let name = 'Kawa Ijen'
|
export let name = 'Kawa Ijen'
|
||||||
export let image = ''
|
export let image = ''
|
||||||
</script>
|
</script>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{location}
|
{city}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import NavigationBarLayout from '../../lib/components/Layouts/NavigationBarLayout.svelte'
|
import NavigationBarLayout from '../../lib/components/Layouts/NavigationBarLayout.svelte'
|
||||||
|
import { Img } from "flowbite-svelte"
|
||||||
import Discover from './Components/Discover.svelte'
|
import Discover from './Components/Discover.svelte'
|
||||||
import Top from './Components/Top.svelte'
|
import Top from './Components/Top.svelte'
|
||||||
import Categories from './Components/Categories.svelte'
|
import Categories from './Components/Categories.svelte'
|
||||||
import Comparment from './Components/Comparment.svelte' //do budoucna bych to udělal pomocí komponent
|
import Comparment from './Components/Comparment.svelte' //do budoucna bych to udělal pomocí komponent
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import Animation from './Components/Animation.svelte'
|
import Animation from './Components/Animation.svelte'
|
||||||
import { getExpiriences } from '$lib/utils/database/game'
|
import { getExperiences } from '$lib/utils/database/experience'
|
||||||
import Loading from '$lib/components/Common/Loading.svelte'
|
import Loading from '$lib/components/Common/Loading.svelte'
|
||||||
|
|
||||||
let fitstTime = false
|
let fitstTime = false
|
||||||
|
@ -35,7 +36,6 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !fitstTime}
|
{#if !fitstTime}
|
||||||
<Top headline={city + state} />
|
<Top headline={city + state} />
|
||||||
|
|
||||||
|
@ -45,12 +45,12 @@
|
||||||
|
|
||||||
<Categories />
|
<Categories />
|
||||||
|
|
||||||
{#await getExpiriences()}
|
{#await getExperiences()}
|
||||||
<div class="w-full h-24 flex items-center justify-center">
|
<div class="w-full h-24 flex items-center justify-center">
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
{:then expiriences}
|
{:then experiences}
|
||||||
<Comparment items={expiriences} />
|
<Comparment items={experiences} />
|
||||||
{/await}
|
{/await}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import LocationRequest from '$lib/components/Map/LocationRequest.svelte'
|
import LocationRequest from '$lib/components/Map/LocationRequest.svelte'
|
||||||
import Marker from '$lib/components/Map/Marker.svelte'
|
import Marker from '$lib/components/Map/Marker.svelte'
|
||||||
import { navigate } from '$lib/router'
|
import { navigate } from '$lib/router'
|
||||||
import { getExpiriencesAsStore } from '$lib/utils/database/game'
|
import { getExperiencesAsStore } from '$lib/utils/database/experience'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
let user: { lat: number; lng: number } = { lat: 0, lng: 0 }
|
let user: { lat: number; lng: number } = { lat: 0, lng: 0 }
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
navigator.geolocation.getCurrentPosition(handleLocationGranted)
|
navigator.geolocation.getCurrentPosition(handleLocationGranted)
|
||||||
|
|
||||||
$: [experiences] = getExpiriencesAsStore()
|
$: [experiences] = getExperiencesAsStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<NavigationBarLayout>
|
<NavigationBarLayout>
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Interests from './../../lib/components/Categories/interests.svelte';
|
||||||
|
// Import necessary modules and components
|
||||||
|
import { user } from '$lib/appwrite';
|
||||||
|
import collections from '$lib/collections';
|
||||||
|
import Loading from '$lib/components/Common/Loading.svelte';
|
||||||
|
import Category2InRow from '$lib/components/categories/Category2InRow.svelte';
|
||||||
|
import Category3InRow from '$lib/components/Categories/preference.svelte';
|
||||||
|
import { getInterests, getTravelBuddies, getRecommendations, addSelectedPreferences } from '$lib/utils/database/preferences';
|
||||||
|
import { navigate } from '$lib/router'
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
interface Preference {
|
||||||
|
name: string;
|
||||||
|
img: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
//define changing state for displaying different categories
|
||||||
|
let current_state = 0;
|
||||||
|
|
||||||
|
// Define variables to store the preferences
|
||||||
|
let interests: Array<Preference> | undefined;
|
||||||
|
let travelBuddies: Array<Preference> | undefined;
|
||||||
|
let recommendations: Array<Preference> | undefined;
|
||||||
|
|
||||||
|
// Define a variable to store the selected preferences
|
||||||
|
let selectedInterests = [];
|
||||||
|
let selectedTravelBuddies = [];
|
||||||
|
let selectedRecommendations = [];
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
interests = await getInterests();
|
||||||
|
travelBuddies = await getTravelBuddies();
|
||||||
|
recommendations = await getRecommendations();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Define a function to handle the form submission
|
||||||
|
async function submitPreferences() {
|
||||||
|
await addSelectedPreferences(user, selectedInterests, selectedTravelBuddies, selectedRecommendations);
|
||||||
|
selectedInterests = [];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if preferences.length > 0}
|
||||||
|
<div class="preferences-page">
|
||||||
|
<div class="progress-bar"></div>
|
||||||
|
<h2>What are your interests?</h2>
|
||||||
|
<Category3InRow
|
||||||
|
interests={interests.slice(0, 3)}
|
||||||
|
onPreferenceSelect={updateSelectedInterests}
|
||||||
|
/>
|
||||||
|
<h2>Who do you like to travel with?</h2>
|
||||||
|
<Category2InRow
|
||||||
|
preferences={preferences.slice(3, 5)}
|
||||||
|
onPreferenceSelect={updateSelectedInterests[Symbol]...}
|
||||||
|
/>
|
||||||
|
<h2>What brought you to our app?</h2>
|
||||||
|
<Category3InRow
|
||||||
|
preferences={preferences.slice(5)}
|
||||||
|
onPreferenceSelect={updateSelected..........}
|
||||||
|
/>
|
||||||
|
<button on:click={submitPreferences}>Submit Preferences</button>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<Loading />
|
||||||
|
{/if}
|
|
@ -0,0 +1,167 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { client } from "$lib/appwrite";
|
||||||
|
import { navigate } from "svelte-routing";
|
||||||
|
import collections from "$lib/collections";
|
||||||
|
import { getInterests, getRecommendations, getTravelBuddies, addSelectedPreferences } from '$lib/utils/database/preferences';
|
||||||
|
import preference from "$lib/components/Categories/preference.svelte";
|
||||||
|
|
||||||
|
|
||||||
|
let preferences = [];
|
||||||
|
|
||||||
|
let selectedPrefId = 1;
|
||||||
|
|
||||||
|
const handleOptionClick = (pref, option) => {
|
||||||
|
pref.selectedOption = option;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNextClick = () => {
|
||||||
|
selectedPrefId += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
const [interests, travelBuddies, recommendedBy] = await Promise.all([
|
||||||
|
getInterests(),
|
||||||
|
getTravelBuddies(),
|
||||||
|
getRecommendations(),
|
||||||
|
]);
|
||||||
|
preferences = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Select your interests",
|
||||||
|
options: interests,
|
||||||
|
selectedOption: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Select your travel buddies",
|
||||||
|
options: travelBuddies,
|
||||||
|
selectedOption: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Who recommended you to this app?",
|
||||||
|
options: recommendedBy,
|
||||||
|
selectedOption: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const userPreferences = {
|
||||||
|
userInterests: interests.map((interest) => interest.name),
|
||||||
|
userTravelBuddy: travelBuddies.map((travelBuddy) => travelBuddy.name),
|
||||||
|
userRecommended: recommendedBy.map((recommendations) => recommendations.name),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await addSelectedPreferences(userPreferences);
|
||||||
|
navigate("/");
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
console.log(await collections.interests.listDocuments());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
setContext("selectedPrefIndex", selectedPrefIndex);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each preferences as pref}
|
||||||
|
{#if pref.id === selectedPrefId}
|
||||||
|
<div class="container">
|
||||||
|
<div class="container_page">
|
||||||
|
<h2>{pref.title}</h2>
|
||||||
|
<div class="radio_container">
|
||||||
|
{#each pref.options as option}
|
||||||
|
<label class="item {selected: preferences[selectedPrefIndex].selectedOption === option}">
|
||||||
|
<input type="radio" name={`pref-${preferences[selectedPrefIndex].id}`} value={option.id} on:change={() => handleOptionClick(preferences[selectedPrefIndex], option)} checked={preferences[selectedPrefIndex].selectedOption === option} />
|
||||||
|
<div>
|
||||||
|
<img src={option.img} alt={option.name} />
|
||||||
|
<p>{option.name}</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{#if pref.selectedOption}
|
||||||
|
<button on:click={handleNextClick}>Next</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{#if selectedPrefId === preferences.length && preferences.every(pref => pref.selectedOption)}
|
||||||
|
<button on:click={addSelectedPreferences}>Save Preferences</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container_page {
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 0px 22px;
|
||||||
|
scroll-snap-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio_container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-self: center;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 7px;
|
||||||
|
width: 145px;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid #4264eb00;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 17px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Interests from "$lib/components/Interests/Interests.svelte"
|
import Interests from "$lib/components/Categories/interests.svelte"
|
||||||
|
|
||||||
let current_state = 1
|
let current_state = 1
|
||||||
</script>
|
</script>
|
||||||
|
@ -50,7 +50,9 @@
|
||||||
.selected{
|
.selected{
|
||||||
transition-delay: 0.25s;
|
transition-delay: 0.25s;
|
||||||
width: 46px;
|
width: 46px;
|
||||||
background-color: #14A6AE;
|
outline: 8px solid #BBD1C5;
|
||||||
|
outline-color: #14A6AE;
|
||||||
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,37 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Line from '$lib/components/Common/Line.svelte'
|
import Line from '$lib/components/Common/Line.svelte';
|
||||||
import SettingRow from '$lib/components/Common/SettingRow.svelte'
|
import SettingRow from '$lib/components/Common/SettingRow.svelte';
|
||||||
import { Link } from 'svelte-routing'
|
import { Link } from 'svelte-routing';
|
||||||
import Earth from '$lib/svg/Earth.svelte'
|
import Earth from '$lib/svg/Earth.svelte';
|
||||||
import Sun from '$lib/svg/Sun.svelte';
|
import Sun from '$lib/svg/Sun.svelte';
|
||||||
|
import PopUp from '$lib/components/Common/PopUp.svelte'
|
||||||
|
|
||||||
|
|
||||||
|
let popUp_Active = false
|
||||||
|
let popUp_Options = []
|
||||||
|
let popUp_Curent = ""
|
||||||
|
|
||||||
|
function popUp_save(answer:string) {
|
||||||
|
if (answer === undefined){
|
||||||
|
popUp_Active = false
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
popUp_Active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PopUp_open(options:any[]){
|
||||||
|
popUp_Options = options
|
||||||
|
popUp_Active=true
|
||||||
|
}
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
itms: [{ icon: Sun, text: 'Theme', link:"/"}, { icon: Earth, text: 'Language', link:"/"}],
|
itms: [{ icon: Sun, text: 'Theme', options:["Dark", "Light"]}, { icon: Earth, text: 'Language', options:["Czech", "English"]}],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Line />
|
<Line />
|
||||||
|
@ -19,13 +40,15 @@
|
||||||
{#if title !== ""}
|
{#if title !== ""}
|
||||||
<div class="mb-4 text-[18px] text-[#61646B]">{title}</div>
|
<div class="mb-4 text-[18px] text-[#61646B]">{title}</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#each itms as { icon, text, link}}
|
{#each itms as { icon, text, options}}
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<svelte:component this={icon} />
|
<svelte:component this={icon} />
|
||||||
<Link class="font-semibold text-[18px]" to={link}>{text}</Link>
|
<button class="font-semibold text-[18px]" on:click={() => PopUp_open(options)} >{text}</button>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
|
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
|
{#if popUp_Active}
|
||||||
|
<PopUp headline="Which language would you like to choose?" options={popUp_Options} on_return={popUp_save} default_ans={popUp_Curent}/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,6 @@
|
||||||
//
|
//
|
||||||
$: [userInfo] = collections.users.getDocument([Query.equal('erantId', params.erantId)])
|
$: [userInfo] = collections.users.getDocument([Query.equal('erantId', params.erantId)])
|
||||||
|
|
||||||
$: console.log($user)
|
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
title: 'Account',
|
title: 'Account',
|
||||||
|
@ -49,6 +47,7 @@
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full h-full p-2">
|
||||||
{#if $userInfo}
|
{#if $userInfo}
|
||||||
<div class="w-full h-[188px] flex flex-wrap flex-col gap-4 justify-center items-center mb-8">
|
<div class="w-full h-[188px] flex flex-wrap flex-col gap-4 justify-center items-center mb-8">
|
||||||
<InputPicture />
|
<InputPicture />
|
||||||
|
@ -80,3 +79,4 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
|
||||||
|
.display1 {
|
||||||
|
/* Display Large - Source Sans Pro/Regular 64/72 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 64px;
|
||||||
|
line-height: 72px; /* or 112% */
|
||||||
|
letter-spacing: -0.25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display2{
|
||||||
|
/* Display Medium - Source Sans Pro/Regular 48/56 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 56px; /* or 117% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.display3{
|
||||||
|
/* Display Small - Source Sans Pro/Regular 40/48 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: 48px; /* or 120% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.h1{
|
||||||
|
/* Headline Large - Source Sans Pro/Regular 32/40 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 40px; /* identical to box height, or 125% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.h2{
|
||||||
|
/* Headline Medium - Source Sans Pro/Regular 28/36 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 36px; /* identical to box height, or 129% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.h3{
|
||||||
|
/* Headline Small - Source Sans Pro/Regular 24/32 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 32px; /* identical to box height, or 133% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.title1{
|
||||||
|
/* Title Large - Source Sans Pro/Medium 22/28 . +0.4 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 30px; /* identical to box height, or 125% */
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title2{
|
||||||
|
/* Title Medium - Source Sans Pro/Medium 16/24 . +0.16 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px; /* identical to box height, or 150% */
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title3{
|
||||||
|
/* Title Small - Source Sans Pro/Medium 14/20 . +0.12 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px; /* identical to box height, or 143% */
|
||||||
|
letter-spacing: 0.16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label1{
|
||||||
|
/* Label Large - Source Sans Pro/Medium 16/24 . +0.2 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 28px; /* identical to box height, or 156% */
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label2{
|
||||||
|
/* Label Medium - Source Sans Pro/Medium 14/20 . +0.4 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px; /* identical to box height, or 150% */
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label3{
|
||||||
|
/* Label Small - Source Sans Pro/Medium 12/16 . +0.6 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px; /* identical to box height, or 143% */
|
||||||
|
letter-spacing: 0.6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body1{
|
||||||
|
/* Body Large - Source Sans Pro/Regular 16/24 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px; /* identical to box height, or 150% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.body2{
|
||||||
|
/* Body Medium - Source Sans Pro/Regular 14/20 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px; /* identical to box height, or 143% */
|
||||||
|
letter-spacing: 0.08px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body3{
|
||||||
|
/* Body Small - Source Sans Pro/Regular 12/16 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px; /* identical to box height, or 133% */
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overline{
|
||||||
|
/* Overline - Source Sans Pro/Regular 12/16 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px; /* identical to box height, or 133% */
|
||||||
|
letter-spacing: 0.8px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caption{
|
||||||
|
/* Caption - Source Sans Pro/Regular 12/16 . 0 */
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px; /* identical to box height, or 133% */
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redacted1{
|
||||||
|
/* Redacted Large */
|
||||||
|
|
||||||
|
font-family: 'Redacted Script';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px; /* identical to box height, or 150% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.redacted2{
|
||||||
|
/* Redacted Medium */
|
||||||
|
|
||||||
|
font-family: 'Redacted Script';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px; /* identical to box height, or 143% */
|
||||||
|
letter-spacing: 0.08px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redacted3{
|
||||||
|
/* Redacted Small */
|
||||||
|
|
||||||
|
font-family: 'Redacted Script';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px; /* identical to box height, or 133% */
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ['./index.html', './src/**/*.{html,svelte,js,ts}', './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'],
|
content: ['./index.html', './src/**/*.{html,svelte,js,ts}', './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {}
|
||||||
},
|
},
|
||||||
plugins: [require('flowbite/plugin')],
|
plugins: [require('flowbite/plugin')],
|
||||||
darkMode: 'class',
|
darkMode: 'class'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue