Test MVP #86
|
@ -1,13 +1,15 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import './main.scss'
|
import './main.scss'
|
||||||
import { i18n, isLoading as localeLoading } from '$lib/locales'
|
import { i18n, isLoading as localeLoading } from '$lib/locales'
|
||||||
import { isLoading as authLoading, user } from '$lib/appwrite'
|
import client, { isLoading as authLoading, databases, user } from '$lib/appwrite'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
import Routes from './__routes.svelte'
|
import Routes from './__routes.svelte'
|
||||||
/** import CookiesPopUp from '$lib/components/Cookies/CookiesPopUp.svelte' */
|
/** import CookiesPopUp from '$lib/components/Cookies/CookiesPopUp.svelte' */
|
||||||
import { navigate } from '$lib/router'
|
import { navigate } from '$lib/router'
|
||||||
import LocationRequest from '$lib/components/Map/LocationRequest.svelte'
|
import LocationRequest from '$lib/components/Map/LocationRequest.svelte'
|
||||||
|
import { Models } from 'appwrite'
|
||||||
|
import { UsersAnswersDocument } from '$lib/TStypes/experiences'
|
||||||
|
|
||||||
let isMounted = false
|
let isMounted = false
|
||||||
$: isReady = $localeLoading === false && $authLoading === false && isMounted
|
$: isReady = $localeLoading === false && $authLoading === false && isMounted
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
export type CheckPoint = {
|
import { Models } from 'appwrite'
|
||||||
$id: string
|
|
||||||
$collectionId: string
|
export interface CheckPoint extends Models.Document {
|
||||||
$createdAt: string
|
|
||||||
$databaseId: string
|
|
||||||
$permissions: string
|
|
||||||
$updatedAt: string
|
$updatedAt: string
|
||||||
CPAfter: string
|
CPAfter: string
|
||||||
CPAnswerID: string
|
CPAnswerID: string
|
||||||
|
@ -17,13 +14,7 @@ export type CheckPoint = {
|
||||||
CPType: 'CHECKBOX' | 'TEXT' | 'INFO' | 'RADIO' | 'NUMBER'
|
CPType: 'CHECKBOX' | 'TEXT' | 'INFO' | 'RADIO' | 'NUMBER'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Experience = {
|
export interface Experience extends Models.Document {
|
||||||
$id: string
|
|
||||||
$collectionId: string
|
|
||||||
$createdAt: string
|
|
||||||
$databaseId: string
|
|
||||||
$permissions: string
|
|
||||||
$updatedAt: string
|
|
||||||
ExpApproved: boolean
|
ExpApproved: boolean
|
||||||
ExpCPsID: string[]
|
ExpCPsID: string[]
|
||||||
ExpCategory?: string
|
ExpCategory?: string
|
||||||
|
@ -41,3 +32,33 @@ export type Experience = {
|
||||||
checkPoints: Array<CheckPoint>
|
checkPoints: Array<CheckPoint>
|
||||||
rating: number
|
rating: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExperienceDocument extends Models.Document {
|
||||||
|
ExpApproved: boolean
|
||||||
|
ExpCPsID: string[]
|
||||||
|
ExpCategory?: string
|
||||||
|
ExpEnd0: string
|
||||||
|
ExpEnd60: string
|
||||||
|
ExpEnd100: string
|
||||||
|
ExpImage?: string
|
||||||
|
ExpIntroduction: string
|
||||||
|
ExpLocation: number[]
|
||||||
|
ExpName: string
|
||||||
|
ExpStart: string
|
||||||
|
ExpTestingCode: string
|
||||||
|
ExpURL: string
|
||||||
|
UserID: string
|
||||||
|
checkPoints: Array<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CPAnswerDocument extends Models.Document {
|
||||||
|
CPAnswer: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UsersAnswersDocument extends Models.Document {
|
||||||
|
userId: string
|
||||||
|
checkPoint: string
|
||||||
|
experience: string
|
||||||
|
correct: boolean
|
||||||
|
attemptCount: number
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
export interface Account {
|
||||||
|
userId: string;
|
||||||
|
userName: string;
|
||||||
|
erantId: string;
|
||||||
|
userImage: string;
|
||||||
|
userInterests: string;
|
||||||
|
userRecommended: string;
|
||||||
|
userTravelBuddy: string;
|
||||||
|
termsAccepted: boolean;
|
||||||
|
}
|
|
@ -6,13 +6,12 @@
|
||||||
export let href: string = ''
|
export let href: string = ''
|
||||||
export let disabled: boolean = false
|
export let disabled: boolean = false
|
||||||
export let primary: boolean = false
|
export let primary: boolean = false
|
||||||
export let style: string = ''
|
|
||||||
|
|
||||||
let className = ''
|
let className = ''
|
||||||
export { className as class }
|
export { className as class }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button {disabled} class:disabled {style} on:click={() => navigate(href)} on:click={() => dispatch('submit', true)} on:click class={className} class:primary>
|
<button {disabled} class:disabled on:click={() => navigate(href)} on:click={() => dispatch('submit', true)} on:click class={className} class:primary>
|
||||||
<slot />
|
<slot />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Alert } from 'flowbite-svelte'
|
||||||
|
export let color: 'red' | 'yellow' | 'green' | 'purple' | 'pink' | 'blue' | 'light' | 'dark' = 'green'
|
||||||
|
export let dismissable = false
|
||||||
|
|
||||||
|
let className = ''
|
||||||
|
export { className as class }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Alert {...$$restProps} {dismissable} class="mt-4 ml-2 mr-2 absolute top-0 z-50 {className}" {color}>
|
||||||
|
<span slot="icon"><slot name="icon" {color} /></span>
|
||||||
|
<span class="text-lg font-medium"><slot name="title" {color} /></span>
|
||||||
|
<div slot="extra">
|
||||||
|
<div class="mt-2 mb-4 text-sm">
|
||||||
|
<slot {color} />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<slot name="buttons" {color} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Alert>
|
|
@ -1,13 +1,26 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import elementIdGenerator from '$lib/utils/elementIdGenerator'
|
import elementIdGenerator from '$lib/utils/elementIdGenerator'
|
||||||
import { Input } from 'flowbite-svelte'
|
import { Input } from 'flowbite-svelte'
|
||||||
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
export let value = ''
|
||||||
|
export let placeholder = ''
|
||||||
|
export let id = elementIdGenerator()
|
||||||
|
export let readOnly = false
|
||||||
|
export let maxLength = Infinity
|
||||||
|
export let icon: string | null | boolean | undefined = null
|
||||||
|
export let iconFunction: 'password' = null
|
||||||
|
export let changedIconOnActive = icon
|
||||||
|
export let iconPosition: 'right' | 'left' = 'left'
|
||||||
|
|
||||||
|
//erant-new
|
||||||
|
export let autocomplete = ''
|
||||||
|
export let pattern: RegExp = null
|
||||||
|
|
||||||
export let value: string = ''
|
|
||||||
export let focus = false
|
|
||||||
export let prefix: string = ''
|
export let prefix: string = ''
|
||||||
export let placeholder: string = ''
|
export let invisiblePrefix = icon ? true : false
|
||||||
export let autocomplete: string = ''
|
|
||||||
const id = elementIdGenerator()
|
|
||||||
export let type:
|
export let type:
|
||||||
| 'color'
|
| 'color'
|
||||||
| 'date'
|
| 'date'
|
||||||
|
@ -29,28 +42,162 @@
|
||||||
| 'search'
|
| 'search'
|
||||||
| 'textarea' = 'text'
|
| 'textarea' = 'text'
|
||||||
|
|
||||||
let className = 'bg-white text-black border-4 border-blue-300 rounded-[25px] text-left text-[30px] p-[16px] outline-none appearance-none w-full'
|
let className: string = ''
|
||||||
export { className as class }
|
export { className as class }
|
||||||
|
|
||||||
const setType = (node: HTMLInputElement) => {
|
$: inputValue = /*pattern ? value.replace(pattern, '') :*/ value //this is here because we always want to equal input.value = value
|
||||||
node.type = type
|
|
||||||
|
$: prefixControl(inputValue) //when inputValue changes => it will check prefix (so when we do $: inputValue = value this func will trigger every moment when inputValue will change and it will overwhite currect input.value thats will be {value})
|
||||||
|
$: patternControl(inputValue) //when inputValue changes => it will check prefix (so when we do $: inputValue = value this func will trigger every moment when inputValue will change and it will overwhite currect input.value thats will be {value})
|
||||||
|
|
||||||
|
const prefixControl = (_e) => {
|
||||||
|
if (prefix) {
|
||||||
|
if (!invisiblePrefix) {
|
||||||
|
if (inputValue?.indexOf(prefix) !== 0) inputValue = `${prefix}${inputValue}`
|
||||||
|
value = inputValue
|
||||||
|
} else if (inputValue?.indexOf(prefix) !== 0) {
|
||||||
|
value = `${prefix}${inputValue}`
|
||||||
|
//inputValue = value.slice(prefix.length, value.length)
|
||||||
|
} else value = inputValue
|
||||||
|
} else {
|
||||||
|
value = inputValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setFocus = (node: HTMLInputElement | HTMLTextAreaElement) => {
|
const patternControl = (_e) => {
|
||||||
focus && node.focus()
|
inputValue = inputValue.replace(pattern, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (prefix && !value.startsWith(prefix)) value = `${prefix}${value}`
|
$: console.log(pattern?.test(inputValue))
|
||||||
|
|
||||||
|
let inputElement: HTMLElement = null
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
prefixControl('')
|
||||||
|
patternControl('')
|
||||||
|
inputElement = document.getElementById(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
let iconActived = false
|
||||||
|
let iconPushedCount = 0
|
||||||
|
|
||||||
|
const iconClick = () => {
|
||||||
|
iconPushedCount++
|
||||||
|
iconActived = iconPushedCount % 2 === 1
|
||||||
|
dispatch('iconClick')
|
||||||
|
if (iconFunction === 'password') {
|
||||||
|
type = type === 'text' ? 'password' : 'text'
|
||||||
|
retype(inputElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const retype = (e: HTMLElement) => {
|
||||||
|
e.setAttribute('type', type)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if type === 'textarea'}
|
<div class="relative w-full">
|
||||||
<textarea {id} {placeholder} use:setFocus bind:value class={className} {...$$restProps} />
|
{#if icon}
|
||||||
{:else}
|
<button
|
||||||
<input {autocomplete} {id} {placeholder} class:prefixPlaceholder={prefix.length === value.length} class={'' + className} use:setType use:setFocus bind:value {...$$restProps} />
|
class={`absolute inset-y-0 ${iconPosition === 'left' ? 'left-0 pl-3' : 'right-0 pr-3'} flex items-center pointer-events-none ${
|
||||||
{/if}
|
!iconFunction ? 'pointer-events-none' : 'pointer-events-auto'
|
||||||
|
} z-50 `}
|
||||||
|
on:click={iconClick}
|
||||||
|
>
|
||||||
|
{#if typeof icon === 'string'}
|
||||||
|
{#if iconActived}
|
||||||
|
{changedIconOnActive}
|
||||||
|
{:else}
|
||||||
|
{icon}
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<slot active={iconActived} />
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<input
|
||||||
|
use:retype
|
||||||
|
{id}
|
||||||
|
class="input rounded-xl p-4 text-gray-900 border border-gray-300 text-left outline-none appearance-none {icon
|
||||||
|
? iconPosition === 'left'
|
||||||
|
? 'pl-12'
|
||||||
|
: 'pr-12'
|
||||||
|
: ''} {className} {readOnly ? 'cursor-pointer' : ''}"
|
||||||
|
{placeholder}
|
||||||
|
bind:value={inputValue}
|
||||||
|
on:input
|
||||||
|
readonly={readOnly ? true : null}
|
||||||
|
maxlength={maxLength}
|
||||||
|
{...$$restProps}
|
||||||
|
{autocomplete}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
{#if icon}
|
||||||
|
{#if iconPosition === 'left'}
|
||||||
|
<Input
|
||||||
|
{autocomplete}
|
||||||
|
{...$$restProps}
|
||||||
|
maxlength={maxLength}
|
||||||
|
readonly={readOnly ? true : null}
|
||||||
|
{id}
|
||||||
|
{type}
|
||||||
|
bind:value={inputValue}
|
||||||
|
class="input rounded-xl p-4 text-gray-900 border border-gray-300 text-left outline-none appearance-none {className} {readOnly ? 'cursor-pointer' : ''}"
|
||||||
|
color={!podm ? 'red' : null}
|
||||||
|
{placeholder}
|
||||||
|
on:input
|
||||||
|
>
|
||||||
|
<button class={`w-auto ${!iconFunction ? 'pointer-events-none' : 'pointer-events-auto'} z-50 `} on:click={iconClick} slot="left">
|
||||||
|
{#if iconActived}
|
||||||
|
{changedIconOnActive}
|
||||||
|
{:else}
|
||||||
|
{icon}
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</Input>
|
||||||
|
{:else}
|
||||||
|
<Input
|
||||||
|
{autocomplete}
|
||||||
|
{...$$restProps}
|
||||||
|
maxlength={maxLength}
|
||||||
|
readonly={readOnly ? true : null}
|
||||||
|
{id}
|
||||||
|
{type}
|
||||||
|
bind:value={inputValue}
|
||||||
|
class="input rounded-xl p-4 text-gray-900 border border-gray-300 text-left outline-none appearance-none {className} {readOnly ? 'cursor-pointer' : ''}"
|
||||||
|
color={!podm ? 'red' : null}
|
||||||
|
{placeholder}
|
||||||
|
on:input
|
||||||
|
>
|
||||||
|
<button class={`w-auto ${!iconFunction ? 'pointer-events-none' : 'pointer-events-auto'} z-50 `} on:click={iconClick} slot="right">
|
||||||
|
{#if iconActived}
|
||||||
|
{changedIconOnActive}
|
||||||
|
{:else}
|
||||||
|
{icon}
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</Input>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<Input
|
||||||
|
{autocomplete}
|
||||||
|
{...$$restProps}
|
||||||
|
maxlength={maxLength}
|
||||||
|
readonly={readOnly ? true : null}
|
||||||
|
{id}
|
||||||
|
{type}
|
||||||
|
bind:value={inputValue}
|
||||||
|
class="input rounded-xl p-4 text-gray-900 border border-gray-300 text-left outline-none appearance-none {className} {readOnly ? 'cursor-pointer' : ''}"
|
||||||
|
color={!podm ? 'red' : null}
|
||||||
|
{placeholder}
|
||||||
|
on:input
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
-->
|
||||||
<style>
|
<style>
|
||||||
input::placeholder {
|
.input::placeholder {
|
||||||
color: #8f8f8f;
|
color: #8f8f8f;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-family: 'Source Sans Pro';
|
font-family: 'Source Sans Pro';
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
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 } from 'svelte'
|
||||||
import LocationRequest from './LocationRequest.svelte'
|
import LocationRequest from './LocationRequest.svelte'
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -12,8 +12,6 @@
|
||||||
export let mapComponent = null
|
export let mapComponent = null
|
||||||
export let userLocation = { lat: 0, lng: 0 }
|
export let userLocation = { lat: 0, lng: 0 }
|
||||||
|
|
||||||
$: console.log(userLocation, center)
|
|
||||||
|
|
||||||
let className = ''
|
let className = ''
|
||||||
export { className as class }
|
export { className as class }
|
||||||
/*;(() => {
|
/*;(() => {
|
||||||
|
@ -59,9 +57,12 @@
|
||||||
options={{
|
options={{
|
||||||
trackUserLocation: true,
|
trackUserLocation: true,
|
||||||
showUserHeading: true,
|
showUserHeading: true,
|
||||||
|
maximumAge: 1500,
|
||||||
positionOptions: {
|
positionOptions: {
|
||||||
enableHighAccuracy: true,
|
enableHighAccuracy: true,
|
||||||
},
|
},
|
||||||
|
showUserLocation: true,
|
||||||
|
showAccuracyCircle: false,
|
||||||
}}
|
}}
|
||||||
bind:trigger
|
bind:trigger
|
||||||
on:trackuserlocationstart={() => {}}
|
on:trackuserlocationstart={() => {}}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Account, Models, Permission, Query, Role } from 'appwrite'
|
||||||
import database from 'svelte-appwrite-client/src/lib/database'
|
import database from 'svelte-appwrite-client/src/lib/database'
|
||||||
import { getLocationDataFromLatAndLong } from '../locations'
|
import { getLocationDataFromLatAndLong } from '../locations'
|
||||||
import { Writable, writable } from 'svelte/store'
|
import { Writable, writable } from 'svelte/store'
|
||||||
import { CheckPoint, Experience } from '$lib/TStypes/experiences'
|
import { CPAnswerDocument, CheckPoint, Experience, UsersAnswersDocument } from '$lib/TStypes/experiences'
|
||||||
import collections from '$lib/collections'
|
import collections from '$lib/collections'
|
||||||
|
|
||||||
export type AnswerState = 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
export type AnswerState = 'wrong-firstTime' | 'wrong-secondTime' | 'correct' | 'not-control' | null
|
||||||
|
@ -13,31 +13,26 @@ userStore.subscribe((res) => (user = res))
|
||||||
|
|
||||||
//Loading of checkpoints and rating is done in the same function to prevent multiple requests to the database
|
//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) => {
|
export const load = async (pathName: string, previewQuestionsCount?: number, preview?: Function) => {
|
||||||
let checkPoints: Array<CheckPoint> = []
|
const experience = (await databases.listDocuments<Models.Document & Experience>('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpURL', pathName)])).documents[0]
|
||||||
// @ts-ignore
|
|
||||||
const experience: Experience = (await databases.listDocuments('63cef30d6da945dd4250', '63cef4bd210fdf2e5888', [Query.equal('ExpURL', pathName)])).documents[0]
|
|
||||||
|
|
||||||
const rating = await getRating(experience.$id)
|
const rating = await getRating(experience.$id)
|
||||||
|
|
||||||
for (let i = 0; i < Math.ceil(experience.ExpCPsID.length / 25); i++) {
|
const checkPointsDocument = (
|
||||||
const { documents, total } = await databases.listDocuments<Models.Document & CheckPoint>('63cef30d6da945dd4250', '63cef84d908acf805758', [
|
await Promise.all(
|
||||||
|
new Array(Math.ceil(experience.ExpCPsID.length / 25)).fill(null).map((_lap, i) => {
|
||||||
|
return databases.listDocuments<Models.Document & CheckPoint>('63cef30d6da945dd4250', '63cef84d908acf805758', [
|
||||||
Query.equal('$id', experience.ExpCPsID),
|
Query.equal('$id', experience.ExpCPsID),
|
||||||
Query.limit(25),
|
Query.limit(25),
|
||||||
Query.offset(i * 25),
|
Query.offset(i * 25),
|
||||||
])
|
])
|
||||||
checkPoints = [...checkPoints, ...documents]
|
}),
|
||||||
}
|
)
|
||||||
|
)
|
||||||
/*if (checkPointsIds.indexOf(checkPointId) === previewQuestionsCount - 1) {
|
.map((listItem) => listItem.documents)
|
||||||
experience['rating'] = rating
|
.flat()
|
||||||
experience['checkPoints'] = checkPoints
|
|
||||||
preview(experience)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
console.log(checkPoints)
|
|
||||||
|
|
||||||
experience['rating'] = rating
|
experience['rating'] = rating
|
||||||
experience['checkPoints'] = checkPoints
|
experience['checkPoints'] = checkPointsDocument
|
||||||
|
|
||||||
return experience
|
return experience
|
||||||
}
|
}
|
||||||
|
@ -62,13 +57,15 @@ export const answer = async (experienceId: string, checkPointId: string, answer:
|
||||||
try {
|
try {
|
||||||
const checkPoint = await databases.getDocument('63cef30d6da945dd4250', '63cef84d908acf805758', checkPointId)
|
const checkPoint = await databases.getDocument('63cef30d6da945dd4250', '63cef84d908acf805758', checkPointId)
|
||||||
const { CPType, CPAnswerID } = checkPoint
|
const { CPType, CPAnswerID } = checkPoint
|
||||||
const correctAnswer = CPType !== 'INFO' ? (await databases.getDocument('63cef30d6da945dd4250', '63dd5c2b764061e40025', CPAnswerID)).CPAnswer : true
|
const correctAnswer =
|
||||||
|
CPType !== 'INFO' ? (await databases.getDocument<Models.Document & CPAnswerDocument>('63cef30d6da945dd4250', '63dd5c2b764061e40025', CPAnswerID)).CPAnswer : null
|
||||||
|
|
||||||
let correct: boolean = false
|
let correct: boolean = false
|
||||||
|
|
||||||
if (CPType === 'CHECKBOX') correct = JSON.stringify(answer) === JSON.stringify(correctAnswer)
|
if (CPType === 'CHECKBOX') correct = JSON.stringify(answer) === JSON.stringify(correctAnswer)
|
||||||
if (CPType === 'RADIO' || CPType === 'NUMBER' || CPType === 'TEXT') correct = answer === correctAnswer[0]
|
if (CPType === 'RADIO' || CPType === 'NUMBER') correct = answer === correctAnswer[0]
|
||||||
if (CPType === 'INFO') correct = false
|
if (CPType === 'INFO') correct = false
|
||||||
|
if (CPType === 'TEXT') correct = correctAnswer[0].localeCompare(answer, 'en', { sensitivity: 'base' }) === 0
|
||||||
await saveAnswerIntoDatabase(experienceId, checkPointId, correct)
|
await saveAnswerIntoDatabase(experienceId, checkPointId, correct)
|
||||||
|
|
||||||
return correct
|
return correct
|
||||||
|
@ -97,7 +94,7 @@ const saveAnswerIntoDatabase = async (experienceId: string, checkPointId: string
|
||||||
experience: experienceId,
|
experience: experienceId,
|
||||||
attemptCount: 1,
|
attemptCount: 1,
|
||||||
},
|
},
|
||||||
[Permission.read(Role.user(user.$id)), Permission.update(Role.user(user.$id))],
|
[Permission.read(Role.user(user.$id)), Permission.update(Role.user(user.$id)), Permission.delete(Role.user(user.$id))],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -144,11 +141,32 @@ export const getExperiencesAsStore = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getUserProgress = async (experienceId: string) => {
|
export const getUserProgress = async (experienceId: string) => {
|
||||||
const { documents } = await databases.listDocuments('63cef30d6da945dd4250', 'users-answers', [Query.equal('userId', user.$id), Query.equal('experience', experienceId)])
|
const { documents, total } = await databases.listDocuments<Models.Document & UsersAnswersDocument>('63cef30d6da945dd4250', 'users-answers', [
|
||||||
return documents
|
Query.equal('userId', user.$id),
|
||||||
|
Query.equal('experience', experienceId),
|
||||||
|
Query.limit(25),
|
||||||
|
Query.offset(0),
|
||||||
|
])
|
||||||
|
|
||||||
|
const allOtherDocuments = (
|
||||||
|
await Promise.all(
|
||||||
|
new Array(Math.ceil((total - 25) / 25) > 0 ? Math.ceil((total - 25) / 25) : 0)
|
||||||
|
.fill(null)
|
||||||
|
.map((_n, i) =>
|
||||||
|
databases.listDocuments<Models.Document & UsersAnswersDocument>('63cef30d6da945dd4250', 'users-answers', [
|
||||||
|
Query.equal('userId', user.$id),
|
||||||
|
Query.equal('experience', experienceId),
|
||||||
|
Query.limit(25),
|
||||||
|
Query.offset((i + 1) * 25),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
).flatMap((list) => list.documents)
|
||||||
|
|
||||||
|
return [...documents, ...allOtherDocuments]
|
||||||
}
|
}
|
||||||
export const getUserProgressAsStore = (experienceId: string) => {
|
export const getUserProgressAsStore = (experienceId: string) => {
|
||||||
const store = writable<Models.Document[]>([])
|
const store = writable<(Models.Document & UsersAnswersDocument)[]>([])
|
||||||
const loading = writable<boolean>(true)
|
const loading = writable<boolean>(true)
|
||||||
getUserProgress(experienceId).then((documents) => {
|
getUserProgress(experienceId).then((documents) => {
|
||||||
store.set(documents)
|
store.set(documents)
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
const urlFail = `${location.origin}/register/failed`
|
const urlFail = `${location.origin}/register/failed`
|
||||||
const urlSuccess = `${location.origin}/create/account`
|
const urlSuccess = `${location.origin}/create/account`
|
||||||
|
|
||||||
|
export let disableLogin = false
|
||||||
|
|
||||||
const login = async (platform: 'facebook' | 'google') => {
|
const login = async (platform: 'facebook' | 'google') => {
|
||||||
|
if (disableLogin) return
|
||||||
try {
|
try {
|
||||||
await user.deleteSessions()
|
await user.deleteSessions()
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
@ -14,12 +17,12 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="continue_with">
|
<div class="continue_with">
|
||||||
<button>
|
<button on:click>
|
||||||
<GoogleLogo />
|
<GoogleLogo />
|
||||||
<p>Continue with Google</p>
|
<p>Continue with Google</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button on:click={() => login('facebook')}>
|
<button on:click on:click={() => login('facebook')}>
|
||||||
<p>Continue with Facebook</p>
|
<p>Continue with Facebook</p>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
import { getErrorMessage } from '../utils/authorizationErrors'
|
import { getErrorMessage } from '../utils/authorizationErrors'
|
||||||
import Button from '$lib/components/Buttons/Button.svelte'
|
import Button from '$lib/components/Buttons/Button.svelte'
|
||||||
import SocialLogin from '../Components/SocialLogin.svelte'
|
import SocialLogin from '../Components/SocialLogin.svelte'
|
||||||
|
import Input from '$lib/components/Inputs/Input.svelte'
|
||||||
|
import Eye from '$lib/svg/Eye.svelte'
|
||||||
|
|
||||||
export let purpose = 'login' //possible values login, register
|
export let purpose = 'login' //possible values login, register
|
||||||
|
|
||||||
|
@ -58,8 +60,18 @@
|
||||||
<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={email} type="text" placeholder="E-mail" autocomplete="email" required />
|
<Input bind:value={email} type="text" placeholder="E-mail" autocomplete="email" class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full" />
|
||||||
<HiddenInput bind:value={password} placeholder="Password" />
|
<Input
|
||||||
|
bind:value={password}
|
||||||
|
let:active
|
||||||
|
iconPosition="right"
|
||||||
|
icon
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full"
|
||||||
|
>
|
||||||
|
<Eye {active} />
|
||||||
|
</Input>
|
||||||
<div class="forgot_password">
|
<div class="forgot_password">
|
||||||
<a href="/forgot-password">Forgot password?</a>
|
<a href="/forgot-password">Forgot password?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,14 +18,13 @@
|
||||||
let repeatPassword = ''
|
let repeatPassword = ''
|
||||||
let name = ''
|
let name = ''
|
||||||
let erantId = ''
|
let erantId = ''
|
||||||
let termsChecked: boolean = false
|
let termsChecked = false
|
||||||
|
let termsCheckboxShake = false
|
||||||
|
$: if (termsCheckboxShake) setTimeout(() => (termsCheckboxShake = false), 750)
|
||||||
|
|
||||||
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 && termsChecked && nicknameIsValid
|
$: buttonCodition = name.length > 0 && email.length > 0 && password.length >= 8 && password === repeatPassword && erantId.length > 2 && termsChecked
|
||||||
|
|
||||||
const pattern = /^[a-zA-Z0-9+@]+$/
|
|
||||||
$: nicknameIsValid = pattern.test(erantId)
|
|
||||||
|
|
||||||
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')
|
||||||
|
@ -52,6 +51,8 @@
|
||||||
state = 'register'
|
state = 'register'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: console.log(erantId)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if state !== 'email-sent'}
|
{#if state !== 'email-sent'}
|
||||||
|
@ -70,14 +71,35 @@
|
||||||
<Input bind:value={name} placeholder="Your name" autocomplete="full-name" class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full" />
|
<Input bind:value={name} placeholder="Your name" autocomplete="full-name" class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full" />
|
||||||
<Input bind:value={email} placeholder="Your e-mail" autocomplete="email" class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full" />
|
<Input bind:value={email} placeholder="Your e-mail" autocomplete="email" class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full" />
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<Input bind:value={erantId} placeholder="@your_nickname" class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full" prefix="@" />
|
<Input
|
||||||
{#if !nicknameIsValid}
|
bind:value={erantId}
|
||||||
<Helper class="flex justify-start w-full pl-4" color="red">Your nickname can include only a-zA-Z0-9 characters</Helper>
|
prefix="@"
|
||||||
{/if}
|
placeholder="your_nickname"
|
||||||
|
class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full"
|
||||||
|
pattern={/[^@a-zA-Z0-9]/g}
|
||||||
|
invisiblePrefix
|
||||||
|
icon="@"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<HiddenInput bind:value={password} placeholder="Password" />
|
<Input
|
||||||
|
iconFunction="password"
|
||||||
|
bind:value={password}
|
||||||
|
placeholder="Password"
|
||||||
|
class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full"
|
||||||
|
inputFunction="password"
|
||||||
|
iconPosition="right"
|
||||||
|
icon="eye"
|
||||||
|
/>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<HiddenInput bind:value={repeatPassword} placeholder="Re-type password" />
|
<Input
|
||||||
|
iconFunction="password"
|
||||||
|
bind:value={repeatPassword}
|
||||||
|
placeholder="Re-type password"
|
||||||
|
class="p-3 border-1 rounded-[15px] bg-[#eeeeee] text-lg w-full"
|
||||||
|
inputFunction="password"
|
||||||
|
iconPosition="right"
|
||||||
|
icon="eye"
|
||||||
|
/>
|
||||||
{#if password !== repeatPassword && password.length >= 8}
|
{#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>
|
<Helper class="flex justify-start w-full pl-4" helperClass="text-sm" color="red">Passwords are not equal</Helper>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -86,7 +108,9 @@
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
bind:checked={termsChecked}
|
bind:checked={termsChecked}
|
||||||
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-5 h-5 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 {termsCheckboxShake
|
||||||
|
? 'animate-bounce border-4 border-red-500'
|
||||||
|
: ''}"
|
||||||
/>
|
/>
|
||||||
<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>,
|
||||||
|
@ -104,7 +128,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SocialLogin />
|
<SocialLogin
|
||||||
|
disableLogin={!termsChecked}
|
||||||
|
on:click={() => {
|
||||||
|
if (!termsChecked) termsCheckboxShake = true
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="LR_switch">
|
<div class="LR_switch">
|
||||||
<p>Already have an account? <a href="/login">Log In</a></p>
|
<p>Already have an account? <a href="/login">Log In</a></p>
|
||||||
|
|
|
@ -46,7 +46,13 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if control === 'not-control' || control === 'correct' || control === 'wrong-secondTime'}
|
{#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>
|
<Button on:submit={() => dispatch('nextQuestion')} primary class="w-3/4 max-w-sm min-w-[400px] h-16 bottom-0 m-10 relative">
|
||||||
|
{#if loading}
|
||||||
|
<Loading />
|
||||||
|
{:else}
|
||||||
|
Na další otázku
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
{:else if control === 'wrong-firstTime' || control === null}
|
{: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">
|
<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}
|
{#if loading}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
export let gameData: Experience
|
export let gameData: Experience
|
||||||
export let client
|
export let client
|
||||||
let score = (client.points / client.possiblePointsToSeize) * 100
|
let score = (client.points / client.possiblePointsToSeize) * 100
|
||||||
console.log(score)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LayoutImg img={gameData.ExpImage}>
|
<LayoutImg img={gameData.ExpImage}>
|
||||||
|
@ -23,6 +22,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Button class="w-80 mt-8 " on:click={() => navigate(-1)}>ukončit hru</Button>
|
<Button class="w-80 mt-8 " on:click={() => navigate('/')}>ukončit hru</Button>
|
||||||
</div>
|
</div>
|
||||||
</LayoutImg>
|
</LayoutImg>
|
||||||
|
|
|
@ -7,9 +7,6 @@
|
||||||
export let checkPoint: CheckPoint
|
export let checkPoint: CheckPoint
|
||||||
export let myAnswer
|
export let myAnswer
|
||||||
|
|
||||||
export let clear: false | true = false
|
|
||||||
$: if (clear) myAnswers = new Array(checkPoint.CPOptions.length).fill(false)
|
|
||||||
|
|
||||||
let myAnswers = new Array(checkPoint.CPOptions.length).fill(false)
|
let myAnswers = new Array(checkPoint.CPOptions.length).fill(false)
|
||||||
$: myAnswer = checkPoint.CPOptions.filter((item, i) => {
|
$: myAnswer = checkPoint.CPOptions.filter((item, i) => {
|
||||||
if (myAnswers[i] === true) return item
|
if (myAnswers[i] === true) return item
|
||||||
|
|
|
@ -13,11 +13,12 @@
|
||||||
import { Experience } from '$lib/TStypes/experiences'
|
import { Experience } from '$lib/TStypes/experiences'
|
||||||
import Layout from '../Components/Layout.svelte'
|
import Layout from '../Components/Layout.svelte'
|
||||||
import { answer, AnswerState, getUserAnswer, getUserProgressAsStore } from '$lib/utils/database/experience'
|
import { answer, AnswerState, getUserAnswer, getUserProgressAsStore } from '$lib/utils/database/experience'
|
||||||
import { user } from '$lib/appwrite'
|
import client, { user } from '$lib/appwrite'
|
||||||
import Button from '$lib/components/Buttons/Button.svelte'
|
import Button from '$lib/components/Buttons/Button.svelte'
|
||||||
import LayoutImg from '$lib/components/Layouts/LayoutImg.svelte'
|
import LayoutImg from '$lib/components/Layouts/LayoutImg.svelte'
|
||||||
import { Models } from 'appwrite'
|
import { Models } from 'appwrite'
|
||||||
import { Writable } from 'svelte/store'
|
import { Writable } from 'svelte/store'
|
||||||
|
import { Button as FlowbiteButton } from 'flowbite-svelte'
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
TEXT: TextForm,
|
TEXT: TextForm,
|
||||||
|
@ -29,29 +30,32 @@
|
||||||
INFO: Info,
|
INFO: Info,
|
||||||
}
|
}
|
||||||
|
|
||||||
$: console.log({ checkPoints: gameData.checkPoints })
|
|
||||||
|
|
||||||
export let control: AnswerState = null
|
export let control: AnswerState = null
|
||||||
let view: 'question' | 'map' | 'end' | 'start' | 'start-map' = 'start-map'
|
let view: 'question' | 'map' | 'end' | 'start' | 'start-map' = 'start-map'
|
||||||
export let gameData: Experience //data
|
export let gameData: Experience //data
|
||||||
$: [userProgress, userProgressLoading] = gameData ? getUserProgressAsStore(gameData?.$id) : []
|
export let userProgress
|
||||||
|
//$: [userProgress, userProgressLoading] = getUserProgressAsStore(gameData.$id)
|
||||||
|
|
||||||
let client = {
|
let client = {
|
||||||
//user data about game
|
//user data about game
|
||||||
pos: 0,
|
pos: 0,
|
||||||
end: gameData.ExpCPsID.length - 1, //kolik otázek
|
end: gameData.ExpCPsID.length - 1, //kolik otázek
|
||||||
points: 0, //body
|
points: 0,
|
||||||
possiblePointsToSeize: gameData.checkPoints.length * 2,
|
possiblePointsToSeize: new Array(gameData.checkPoints.length).fill(2).reduce((accumulator, currentValue, index) => {
|
||||||
|
if (gameData.checkPoints[index].CPType !== 'INFO') return accumulator + currentValue
|
||||||
|
else return accumulator
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
$: if (client.pos < $userProgress?.length - 1 + 1) {
|
|
||||||
|
$: if (client.pos < userProgress?.length - 1 + 1) {
|
||||||
// nastaví na continue
|
// nastaví na continue
|
||||||
client.pos = $userProgress?.length - 1 + 1
|
client.pos = userProgress?.length - 1 + 1
|
||||||
|
client.points = userProgress?.map((i) => (i.correct ? 2 : 0))?.reduce((accumulator, currentValue) => accumulator + currentValue)
|
||||||
|
|
||||||
view = 'map'
|
view = 'map'
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (gameData.checkPoints[client.pos].CPType === 'INFO' && !$userProgressLoading) control = 'not-control'
|
$: if (gameData.checkPoints[client.pos].CPType === 'INFO') control = 'not-control'
|
||||||
|
|
||||||
$: console.log(control)
|
|
||||||
|
|
||||||
const nextQuestion = () => {
|
const nextQuestion = () => {
|
||||||
//další otázka
|
//další otázka
|
||||||
|
@ -64,7 +68,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$: checkPoint = gameData.checkPoints[client.pos]
|
$: checkPoint = gameData.checkPoints[client.pos]
|
||||||
$: checkPointType = checkPoint.CPType
|
$: checkPointType = checkPoint?.CPType
|
||||||
|
|
||||||
let page = null
|
let page = null
|
||||||
$: page = view === 'question' ? components[checkPointType] : null
|
$: page = view === 'question' ? components[checkPointType] : null
|
||||||
|
@ -97,14 +101,26 @@
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
|
myAnswer = ''
|
||||||
answerLoading = false
|
answerLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const admins = [
|
||||||
|
'641b2cd262519fdd33ec',
|
||||||
|
'643bfde664ea0c643112',
|
||||||
|
'643bfdd8c64e75d0b8ea',
|
||||||
|
'6427218926d6ab7f8e52',
|
||||||
|
'641d5642c8fa96066cf2',
|
||||||
|
'641b42847ac86f9a306c',
|
||||||
|
'63daafd3355edb14483d',
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input type="number" bind:value={client.pos} />
|
|
||||||
<button on:click={() => (view = view === 'start-map' ? 'start' : 'question')}>disappear map</button>
|
|
||||||
|
|
||||||
{#if view === 'map' || view === 'start-map'}
|
{#if view === 'map' || view === 'start-map'}
|
||||||
|
{#if admins.includes($user.$id)}<!--only if admin-->
|
||||||
|
<FlowbiteButton class="absolute z-50" color="red" on:click={() => (view = view === 'start-map' ? 'start' : 'question')}>disappear map</FlowbiteButton>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<Erantmap bind:userLocation class="w-full h-full">
|
<Erantmap bind:userLocation class="w-full h-full">
|
||||||
<Marker on:enter={() => (view = view === 'start-map' ? 'start' : 'question')} {lat} {lng} {userLocation} />
|
<Marker on:enter={() => (view = view === 'start-map' ? 'start' : 'question')} {lat} {lng} {userLocation} />
|
||||||
</Erantmap>
|
</Erantmap>
|
||||||
|
|
|
@ -6,32 +6,56 @@
|
||||||
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/stores'
|
|
||||||
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 Marker from '../../lib/components/Map/Marker.svelte'
|
import Marker from '../../lib/components/Map/Marker.svelte'
|
||||||
import { onMount } from 'svelte'
|
import { getExperienceIdByUrlAsStore, getUserProgressAsStore, loadAsStore } from '$lib/utils/database/experience'
|
||||||
import { getExperienceIdByUrl, getExperienceIdByUrlAsStore, getUserProgress, getUserProgressAsStore, load, loadAsStore } from '$lib/utils/database/experience'
|
|
||||||
import { getLocationDataFromLatAndLong } from '$lib/utils/locations'
|
import { getLocationDataFromLatAndLong } from '$lib/utils/locations'
|
||||||
import { navigate } from '$lib/router'
|
|
||||||
import { Experience } from '$lib/TStypes/experiences'
|
import { Experience } from '$lib/TStypes/experiences'
|
||||||
import Progressbar from '$lib/components/erant/Progressbar.svelte'
|
import Progressbar from '$lib/components/erant/Progressbar.svelte'
|
||||||
import { writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
|
import Alert from '$lib/components/Common/Alert.svelte'
|
||||||
|
import { Button as FlowbiteButton } from 'flowbite-svelte'
|
||||||
|
import { databases, user } from '$lib/appwrite'
|
||||||
|
import { Query } from 'appwrite'
|
||||||
|
|
||||||
export let params: { gameurl: string }
|
export let params: { gameurl: string }
|
||||||
let gameData = writable<Experience>(null)
|
let gameData = writable<Experience>(null)
|
||||||
let [id] = getExperienceIdByUrlAsStore(params.gameurl)
|
let [id] = getExperienceIdByUrlAsStore(params.gameurl)
|
||||||
$: [userProgress] = $gameData ? getUserProgressAsStore($id) : []
|
$: [userProgress, userProgressLoading] = $gameData ? getUserProgressAsStore($id) : []
|
||||||
|
|
||||||
$: [gameData] = $gameData ? [] : loadAsStore(params.gameurl)
|
$: [gameData] = $gameData ? [] : loadAsStore(params.gameurl)
|
||||||
$: if ($gameData) view = 'experience-preview'
|
$: if ($gameData && !$userProgressLoading) view = 'experience-preview'
|
||||||
|
|
||||||
let view: 'experience-play' | 'experience-loading' | 'experience-preview' = 'experience-loading'
|
let view: 'experience-play' | 'experience-loading' | 'experience-preview' = 'experience-loading'
|
||||||
|
|
||||||
|
const deleteProgress = async () => {
|
||||||
|
const documentsToDelete = $userProgress.map(({ $id }) => databases.deleteDocument('63cef30d6da945dd4250', 'users-answers', $id))
|
||||||
|
const res = await Promise.all(documentsToDelete)
|
||||||
|
deleteProgressAlertVisible = false
|
||||||
|
$userProgress = []
|
||||||
|
}
|
||||||
|
|
||||||
|
let deleteProgressAlertVisible = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if deleteProgressAlertVisible}
|
||||||
|
<Alert color="red" let:color>
|
||||||
|
<span slot="title">Delete your progress</span>
|
||||||
|
<div class="text-black">
|
||||||
|
<span>Are you sure that you want to delete your progress in experience {$gameData?.ExpName}</span>
|
||||||
|
<span>Experience Id: {$gameData?.$id}</span>
|
||||||
|
<span>Your current score is: {$userProgress?.length} checkPoints</span>
|
||||||
|
</div>
|
||||||
|
<div slot="buttons">
|
||||||
|
<FlowbiteButton on:click={deleteProgress} {color}>Yes I'm sure</FlowbiteButton>
|
||||||
|
<FlowbiteButton on:click={() => (deleteProgressAlertVisible = false)} outline {color}>No, I missed clicked</FlowbiteButton>
|
||||||
|
</div>
|
||||||
|
</Alert>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if view === 'experience-loading'}
|
{#if view === 'experience-loading'}
|
||||||
<h1 class="flex items-center justify-center flex-col">
|
<h1 class="flex items-center justify-center flex-col h-full">
|
||||||
<span>Experience is loading...</span>
|
<span>Experience is loading...</span>
|
||||||
<Loading />
|
<Loading />
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -54,20 +78,20 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $gameData.ExpIntroduction}
|
{#if $gameData.ExpIntroduction}
|
||||||
<Section title="Popis">
|
<Section title="Description">
|
||||||
<span>
|
<span>
|
||||||
{@html $gameData.ExpIntroduction}
|
{@html $gameData.ExpIntroduction}
|
||||||
</span>
|
</span>
|
||||||
</Section>
|
</Section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Section title="Fotky">
|
<!--<Section title="Fotky">
|
||||||
<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);">
|
||||||
<!--<ImageSlider images={assets} />-->
|
<ImageSlider images={assets} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>-->
|
||||||
<Section title="Progress">
|
<Section title="Progress">
|
||||||
<Progressbar max={$gameData.ExpCPsID.length} progress={$userProgress.length} showWrittenProgress />
|
<Progressbar max={$gameData.ExpCPsID.length} progress={$userProgress.length} showWrittenProgress />
|
||||||
</Section>
|
</Section>
|
||||||
|
@ -82,12 +106,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $gameData.checkPoints.length}
|
{#if $gameData.checkPoints.length && !($gameData.ExpCPsID.length === $userProgress.length)}
|
||||||
<Button on:click={() => (view = 'experience-play')} primary>{$userProgress.length ? 'Pokračovat' : 'Hrát'}</Button>
|
<Button on:click={() => (view = 'experience-play')} primary>{$userProgress.length ? 'Pokračovat' : 'Hrát'}</Button>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if $userProgress.length}
|
||||||
|
<Button class="!bg-red-500" on:click={() => (deleteProgressAlertVisible = true)} primary>resetovat progress</Button>
|
||||||
|
{/if}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
{:else if view === 'experience-play'}
|
{:else if view === 'experience-play'}
|
||||||
<Renderer gameData={$gameData} />
|
<Renderer userProgress={$userProgress} gameData={$gameData} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
Loading…
Reference in New Issue