mirror of
https://github.com/wasnertobias/StalkingAId.git
synced 2025-10-16 11:55:43 +00:00
Delete unused components, add chat frontend
This commit is contained in:
parent
1bc80ed7d2
commit
f5e98edaa8
@ -23,6 +23,11 @@ app.route("/chat").post((req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.length === 0) {
|
||||
res.status(200).end(true);
|
||||
return;
|
||||
}
|
||||
|
||||
getResponse(history, msg, res);
|
||||
});
|
||||
|
||||
@ -63,7 +68,7 @@ async function getResponse(history, msg, res) {
|
||||
history = history + '\nHuman: ' + msg;
|
||||
|
||||
const prompt = staticPrefix + '\n' + history;
|
||||
console.log("sending to openai: " + prompt);
|
||||
// console.log("sending to openai: " + prompt);
|
||||
|
||||
try {
|
||||
const response = await openai.createCompletion({
|
||||
@ -77,7 +82,7 @@ async function getResponse(history, msg, res) {
|
||||
stop: [" Human:", " AI:"],
|
||||
});
|
||||
const stringResponse = JSON.stringify(response.data);
|
||||
console.log("got from openai: " + stringResponse);
|
||||
// console.log("got from openai: " + stringResponse);
|
||||
res.status(200).end(history + response.data.choices[0].text);
|
||||
} catch(error) {
|
||||
console.error(error.response.status, error.response.data);
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
"postcss-jit-props": "^1.0.9",
|
||||
"sass": "^1.57.1",
|
||||
"vite": "^4.0.0",
|
||||
"autoprefixer": "10.4.13"
|
||||
"autoprefixer": "10.4.13",
|
||||
"jquery": "3.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ specifiers:
|
||||
eslint: ^8.28.0
|
||||
eslint-config-prettier: ^8.5.0
|
||||
eslint-plugin-svelte3: ^4.0.0
|
||||
jquery: 3.6.3
|
||||
open-props: ^1.5.3
|
||||
postcss: ^8.4.21
|
||||
postcss-jit-props: ^1.0.9
|
||||
@ -28,6 +29,7 @@ dependencies:
|
||||
'@poppanator/sveltekit-svg': 2.0.2_svelte@3.55.0+vite@4.0.4
|
||||
'@sveltejs/adapter-node': 1.1.0_@sveltejs+kit@1.0.7
|
||||
autoprefixer: 10.4.13_postcss@8.4.21
|
||||
jquery: 3.6.3
|
||||
open-props: 1.5.3
|
||||
postcss: 8.4.21
|
||||
postcss-jit-props: 1.0.9_postcss@8.4.21
|
||||
@ -1351,6 +1353,10 @@ packages:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
dev: true
|
||||
|
||||
/jquery/3.6.3:
|
||||
resolution: {integrity: sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==}
|
||||
dev: false
|
||||
|
||||
/js-sdsl/4.2.0:
|
||||
resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==}
|
||||
dev: true
|
||||
|
||||
@ -1,127 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import AutocompleteItem from './AutocompleteItem.svelte';
|
||||
|
||||
export let options: string[] = ['test-1', 'test-2', 'test-3'];
|
||||
export let placeholder: string | undefined = undefined;
|
||||
|
||||
const dispatch = createEventDispatcher<{ select: string | undefined }>();
|
||||
|
||||
/* FILTERING countres DATA BASED ON INPUT */
|
||||
let filteredCountries: string[] = [];
|
||||
// $: console.log(filteredCountries)
|
||||
|
||||
const filterNace = () => {
|
||||
let storageArr: string[] = [];
|
||||
if (inputValue) {
|
||||
options.forEach((option) => {
|
||||
const matchIndex = option.toLowerCase().indexOf(inputValue.toLowerCase());
|
||||
if (matchIndex !== -1) {
|
||||
storageArr = [...storageArr, makeMatchBold(option, matchIndex)];
|
||||
}
|
||||
});
|
||||
}
|
||||
filteredCountries = storageArr;
|
||||
};
|
||||
|
||||
/* HANDLING THE INPUT */
|
||||
let searchInput: HTMLInputElement; // use with bind:this to focus element
|
||||
let inputValue = '';
|
||||
|
||||
$: if (!inputValue) {
|
||||
filteredCountries = [];
|
||||
hiLiteIndex = null;
|
||||
}
|
||||
|
||||
const clearInput = () => {
|
||||
inputValue = '';
|
||||
searchInput.focus();
|
||||
};
|
||||
|
||||
const setInputVal = (categoryName: string) => {
|
||||
inputValue = removeBold(categoryName);
|
||||
filteredCountries = [];
|
||||
hiLiteIndex = null;
|
||||
document.querySelector('#nace-input').focus();
|
||||
|
||||
dispatch('select', inputValue);
|
||||
};
|
||||
|
||||
const makeMatchBold = (str: string, start: number) => {
|
||||
// replace part of (country name === inputValue) with strong tags
|
||||
let matched = str.substring(start, start + inputValue.length);
|
||||
let makeBold = `<strong>${matched}</strong>`;
|
||||
let boldedMatch = str.replace(matched, makeBold);
|
||||
return boldedMatch;
|
||||
};
|
||||
|
||||
const removeBold = (str: string) => {
|
||||
//replace < and > all characters between
|
||||
return str.replace(/<(.)*?>/g, '');
|
||||
// return str.replace(/<(strong)>/g, "").replace(/<\/(strong)>/g, "");
|
||||
};
|
||||
|
||||
/* NAVIGATING OVER THE LIST OF COUNTRIES W HIGHLIGHTING */
|
||||
let hiLiteIndex: null | number = null;
|
||||
//$: console.log(hiLiteIndex);
|
||||
$: hiLitedCountry = filteredCountries[hiLiteIndex];
|
||||
|
||||
const navigateList = (e) => {
|
||||
if (e.key === 'ArrowDown' && hiLiteIndex <= filteredCountries.length - 1) {
|
||||
hiLiteIndex === null ? (hiLiteIndex = 0) : (hiLiteIndex += 1);
|
||||
} else if (e.key === 'ArrowUp' && hiLiteIndex !== null) {
|
||||
hiLiteIndex === 0 ? (hiLiteIndex = filteredCountries.length - 1) : (hiLiteIndex -= 1);
|
||||
} else if (e.key === 'Enter') {
|
||||
setInputVal(filteredCountries[hiLiteIndex]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={navigateList} />
|
||||
|
||||
<div class="autocomplete">
|
||||
<input
|
||||
id="nace-input"
|
||||
type="text"
|
||||
autocomplete=""
|
||||
{placeholder}
|
||||
bind:this={searchInput}
|
||||
bind:value={inputValue}
|
||||
on:input={filterNace}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- FILTERED LIST OF COUNTRIES -->
|
||||
{#if filteredCountries.length > 0}
|
||||
<ul id="autocomplete-items-list">
|
||||
{#each filteredCountries as country, i}
|
||||
<AutocompleteItem
|
||||
itemLabel={country}
|
||||
highlighted={i === hiLiteIndex}
|
||||
on:click={() => setInputVal(country)}
|
||||
/>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
div.autocomplete {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#autocomplete-items-list {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: -11px;
|
||||
border: 2px solid rgba(var(--color-foreground-light-raw), 0.2);
|
||||
background-color: var(--color-background);
|
||||
border-bottom-left-radius: var(--size-3);
|
||||
border-bottom-right-radius: var(--size-3);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-5);
|
||||
}
|
||||
</style>
|
||||
@ -1,45 +0,0 @@
|
||||
<script>
|
||||
export let itemLabel;
|
||||
export let highlighted;
|
||||
</script>
|
||||
|
||||
<li class="autocomplete-items" class:autocomplete-active={highlighted} on:click>
|
||||
{@html itemLabel}
|
||||
</li>
|
||||
|
||||
<style lang="scss">
|
||||
li.autocomplete-items {
|
||||
display: block;
|
||||
list-style: none;
|
||||
border-bottom: 1px solid rgba(var(--color-foreground-raw), 0.2);
|
||||
z-index: 99;
|
||||
/*position the autocomplete items to be the same width as the container:*/
|
||||
width: 100%;
|
||||
padding: var(--size-2) var(--size-4);
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
|
||||
max-inline-size: unset;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
li.autocomplete-items:hover {
|
||||
/*when hovering an item:*/
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
li.autocomplete-items:active {
|
||||
/*when navigating through the items using the arrow keys:*/
|
||||
background-color: var(--color-primary) !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
.autocomplete-active {
|
||||
/*when navigating through the items using the arrow keys:*/
|
||||
background-color: var(--color-primary) !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
@ -1,127 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Card from '../Card.svelte';
|
||||
import type { RadioOption } from './types';
|
||||
|
||||
// based on suggestions from:
|
||||
// Sami Keijonen https://webdesign.tutsplus.com/tutorials/how-to-make-custom-accessible-checkboxes-and-radio-buttons--cms-32074
|
||||
// and Inclusive Components by Heydon Pickering https://inclusive-components.design/toggle-button/
|
||||
|
||||
export let options: RadioOption[] = [];
|
||||
export let legend: string | undefined = undefined;
|
||||
export let userSelected: string | undefined = undefined;
|
||||
export let fontSize = 16;
|
||||
export let flexDirection = 'column';
|
||||
|
||||
const uniqueID = Math.floor(Math.random() * 100);
|
||||
|
||||
const slugify = (str = '') => str.toLowerCase().replace(/ /g, '-').replace(/\./g, '');
|
||||
</script>
|
||||
|
||||
<div
|
||||
role="radiogroup"
|
||||
class="group-container"
|
||||
aria-labelledby={`label-${uniqueID}`}
|
||||
id={`group-${uniqueID}`}
|
||||
>
|
||||
{#each options as { value, label, icon }}
|
||||
<label for={slugify(label)}>
|
||||
<Card class={'radio-wrap' + (value === userSelected ? ' selected' : '')}>
|
||||
<span class="icon">{icon}</span>
|
||||
<input class="sr-only" type="radio" id={slugify(label)} bind:group={userSelected} {value} />
|
||||
{label}
|
||||
</Card>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use 'sass:math';
|
||||
|
||||
@import '$lib/style/app.scss';
|
||||
|
||||
$dot-size: 1.5rem;
|
||||
|
||||
:global(.radio-wrap) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 1.5rem;
|
||||
border: 5px solid var(--color-background) !important;
|
||||
|
||||
.icon {
|
||||
font-size: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.card.selected) {
|
||||
border: 5px solid var(--color-primary) !important;
|
||||
}
|
||||
|
||||
.group-container {
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
@media (min-width: $breakpoint-small) {
|
||||
flex-direction: row;
|
||||
}
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--size-4);
|
||||
}
|
||||
|
||||
.legend {
|
||||
font-weight: bold;
|
||||
}
|
||||
label {
|
||||
width: 100%;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
margin-bottom: var(--size-4);
|
||||
|
||||
:global(.card) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input[type='radio'] {
|
||||
position: relative;
|
||||
|
||||
&:checked {
|
||||
& + label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
& + label::before {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
& + label::after {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type='radio']:disabled + label {
|
||||
color: rgba(var(--color-foreground-raw), 0.2);
|
||||
}
|
||||
|
||||
input[type='radio']:disabled + label::before {
|
||||
background: rgba(var(--color-foreground-raw), 0.2);
|
||||
}
|
||||
</style>
|
||||
@ -1 +0,0 @@
|
||||
export type RadioOption = { value: string; label: string; icon: string };
|
||||
@ -1,304 +0,0 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fly, fade } from 'svelte/transition';
|
||||
|
||||
// Props
|
||||
export let min = 1;
|
||||
export let max = 10000;
|
||||
export let initialValue = 0;
|
||||
export let id = null;
|
||||
export let value = typeof initialValue === 'string' ? parseInt(initialValue) : initialValue;
|
||||
|
||||
// Node Bindings
|
||||
let container = null;
|
||||
let thumb = null;
|
||||
let progressBar = null;
|
||||
let element = null;
|
||||
|
||||
// Internal State
|
||||
let elementX = null;
|
||||
let currentThumb = null;
|
||||
let holding = false;
|
||||
let thumbHover = false;
|
||||
let keydownAcceleration = 0;
|
||||
let accelerationTimer = null;
|
||||
|
||||
// Dispatch 'change' events
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
// Mouse shield used onMouseDown to prevent any mouse events penetrating other elements,
|
||||
// ie. hover events on other elements while dragging. Especially for Safari
|
||||
const mouseEventShield = document.createElement('div');
|
||||
mouseEventShield.setAttribute('class', 'mouse-over-shield');
|
||||
mouseEventShield.addEventListener('mouseover', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
function resizeWindow() {
|
||||
elementX = element.getBoundingClientRect().left;
|
||||
}
|
||||
|
||||
// Allows both bind:value and on:change for parent value retrieval
|
||||
function setValue(val) {
|
||||
value = val;
|
||||
dispatch('change', { value });
|
||||
}
|
||||
|
||||
function onTrackEvent(e) {
|
||||
// Update value immediately before beginning drag
|
||||
updateValueOnEvent(e);
|
||||
onDragStart(e);
|
||||
}
|
||||
|
||||
function onHover(e) {
|
||||
thumbHover = thumbHover ? false : true;
|
||||
}
|
||||
|
||||
function onDragStart(e) {
|
||||
// If mouse event add a pointer events shield
|
||||
if (e.type === 'mousedown') document.body.append(mouseEventShield);
|
||||
currentThumb = thumb;
|
||||
}
|
||||
|
||||
function onDragEnd(e) {
|
||||
// If using mouse - remove pointer event shield
|
||||
if (e.type === 'mouseup') {
|
||||
if (document.body.contains(mouseEventShield)) document.body.removeChild(mouseEventShield);
|
||||
// Needed to check whether thumb and mouse overlap after shield removed
|
||||
if (isMouseInElement(e, thumb)) thumbHover = true;
|
||||
}
|
||||
currentThumb = null;
|
||||
}
|
||||
|
||||
// Check if mouse event cords overlay with an element's area
|
||||
function isMouseInElement(event, element) {
|
||||
let rect = element.getBoundingClientRect();
|
||||
let { clientX: x, clientY: y } = event;
|
||||
if (x < rect.left || x >= rect.right) return false;
|
||||
if (y < rect.top || y >= rect.bottom) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Accessible keypress handling
|
||||
function onKeyPress(e) {
|
||||
// Max out at +/- 10 to value per event (50 events / 5)
|
||||
// 100 below is to increase the amount of events required to reach max velocity
|
||||
if (keydownAcceleration < 50) keydownAcceleration++;
|
||||
let throttled = Math.ceil(keydownAcceleration / 5);
|
||||
|
||||
if (e.key === 'ArrowUp' || e.key === 'ArrowRight') {
|
||||
if (value + throttled > max || value >= max) {
|
||||
setValue(max);
|
||||
} else {
|
||||
setValue(value + throttled);
|
||||
}
|
||||
}
|
||||
if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') {
|
||||
if (value - throttled < min || value <= min) {
|
||||
setValue(min);
|
||||
} else {
|
||||
setValue(value - throttled);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset acceleration after 100ms of no events
|
||||
clearTimeout(accelerationTimer);
|
||||
accelerationTimer = setTimeout(() => (keydownAcceleration = 1), 100);
|
||||
}
|
||||
|
||||
function calculateNewValue(clientX) {
|
||||
// Find distance between cursor and element's left cord (20px / 2 = 10px) - Center of thumb
|
||||
let delta = clientX - (elementX + 10);
|
||||
|
||||
// Use width of the container minus (5px * 2 sides) offset for percent calc
|
||||
let percent = (delta * 100) / (container.clientWidth - 10);
|
||||
|
||||
// Limit percent 0 -> 100
|
||||
percent = percent < 0 ? 0 : percent > 100 ? 100 : percent;
|
||||
|
||||
// Limit value min -> max
|
||||
setValue(parseInt((percent * (max - min)) / 100) + min);
|
||||
}
|
||||
|
||||
// Handles both dragging of touch/mouse as well as simple one-off click/touches
|
||||
function updateValueOnEvent(e) {
|
||||
// touchstart && mousedown are one-off updates, otherwise expect a currentPointer node
|
||||
if (!currentThumb && e.type !== 'touchstart' && e.type !== 'mousedown') return false;
|
||||
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
|
||||
// Get client's x cord either touch or mouse
|
||||
const clientX =
|
||||
e.type === 'touchmove' || e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
|
||||
|
||||
calculateNewValue(clientX);
|
||||
}
|
||||
|
||||
// React to left position of element relative to window
|
||||
$: if (element) elementX = element.getBoundingClientRect().left;
|
||||
|
||||
// Set a class based on if dragging
|
||||
$: holding = Boolean(currentThumb);
|
||||
|
||||
// Update progressbar and thumb styles to represent value
|
||||
$: if (progressBar && thumb) {
|
||||
// Limit value min -> max
|
||||
value = value > min ? value : min;
|
||||
value = value < max ? value : max;
|
||||
|
||||
let percent = ((value - min) * 100) / (max - min);
|
||||
let offsetLeft = (container.clientWidth - 10) * (percent / 100) + 5;
|
||||
|
||||
// Update thumb position + active range track width
|
||||
thumb.style.left = `${offsetLeft}px`;
|
||||
progressBar.style.width = `${offsetLeft}px`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:touchmove|nonpassive={updateValueOnEvent}
|
||||
on:touchcancel={onDragEnd}
|
||||
on:touchend={onDragEnd}
|
||||
on:mousemove={updateValueOnEvent}
|
||||
on:mouseup={onDragEnd}
|
||||
on:resize={resizeWindow}
|
||||
/>
|
||||
<div class="range">
|
||||
<div
|
||||
class="range__wrapper"
|
||||
tabindex="0"
|
||||
on:keydown={onKeyPress}
|
||||
bind:this={element}
|
||||
role="slider"
|
||||
aria-valuemin={min}
|
||||
aria-valuemax={max}
|
||||
aria-valuenow={value}
|
||||
{id}
|
||||
on:mousedown={onTrackEvent}
|
||||
on:touchstart={onTrackEvent}
|
||||
>
|
||||
<div class="range__track" bind:this={container}>
|
||||
<div class="range__track--highlighted" bind:this={progressBar} />
|
||||
<div
|
||||
class="range__thumb"
|
||||
class:range__thumb--holding={holding}
|
||||
bind:this={thumb}
|
||||
on:touchstart={onDragStart}
|
||||
on:mousedown={onDragStart}
|
||||
on:mouseover={() => (thumbHover = true)}
|
||||
on:mouseout={() => (thumbHover = false)}
|
||||
>
|
||||
{#if holding || thumbHover}
|
||||
<div class="range__tooltip" in:fly={{ y: 7, duration: 200 }} out:fade={{ duration: 100 }}>
|
||||
{value}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svelte:head>
|
||||
<style>
|
||||
.mouse-over-shield {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgba(255, 0, 0, 0);
|
||||
z-index: 10000;
|
||||
cursor: grabbing;
|
||||
}
|
||||
</style>
|
||||
</svelte:head>
|
||||
|
||||
<style>
|
||||
.range {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.range__wrapper {
|
||||
min-width: 100%;
|
||||
position: relative;
|
||||
padding: 0.5rem;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.range__wrapper:focus-visible > .range__track {
|
||||
box-shadow: 0 0 0 2px white, 0 0 0 3px var(--color-primary);
|
||||
}
|
||||
|
||||
.range__track {
|
||||
height: 6px;
|
||||
background-color: rgba(var(--color-foreground-raw), 0.1);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.range__track--highlighted {
|
||||
background-color: var(--color-primary);
|
||||
width: 0;
|
||||
height: 6px;
|
||||
position: absolute;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.range__thumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
border-radius: 999px;
|
||||
margin-top: -8px;
|
||||
transition: box-shadow 100ms;
|
||||
user-select: none;
|
||||
box-shadow: var(
|
||||
--thumb-boxshadow,
|
||||
0 1px 1px 0 rgba(0, 0, 0, 0.14),
|
||||
0 0px 2px 1px rgba(0, 0, 0, 0.2)
|
||||
);
|
||||
}
|
||||
|
||||
.range__thumb--holding {
|
||||
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 2px 1px rgba(0, 0, 0, 0.2),
|
||||
0 0 0 6px var(--thumb-holding-outline, rgba(113, 119, 250, 0.3));
|
||||
}
|
||||
|
||||
.range__tooltip {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: -50px;
|
||||
color: var(--tooltip-text, white);
|
||||
width: 120px;
|
||||
padding: 4px 0;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
background-color: var(--color-primary);
|
||||
background: var(
|
||||
--tooltip-bg,
|
||||
linear-gradient(45deg, var(--color-primary), var(--color-primary))
|
||||
);
|
||||
}
|
||||
|
||||
.range__tooltip::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 7px;
|
||||
width: 7px;
|
||||
background-color: var(--color-primary);
|
||||
bottom: -3px;
|
||||
left: calc(50% - 3px);
|
||||
clip-path: polygon(0% 0%, 100% 100%, 0% 100%);
|
||||
transform: rotate(-45deg);
|
||||
border-radius: 0 0 0 3px;
|
||||
}
|
||||
</style>
|
||||
@ -1,86 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let stepCount: number = 4;
|
||||
export let currentStep: number = 4;
|
||||
export let enabledStep: number = 4;
|
||||
|
||||
const onStepClick = (idx: number) => {
|
||||
if (idx >= enabledStep) {
|
||||
return;
|
||||
}
|
||||
currentStep = idx;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="stepper">
|
||||
{#each Array(stepCount) as _, i}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:click={() => onStepClick(i + 1)}
|
||||
class="step"
|
||||
class:completed={i < currentStep}
|
||||
class:active={currentStep === i + 1}
|
||||
>
|
||||
{i + 1}
|
||||
</div>
|
||||
{#if i < stepCount - 1}
|
||||
<div class="dash" class:completed={i < currentStep} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import '$lib/style/app.scss';
|
||||
.stepper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
gap: var(--size-2);
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
gap: var(--size-4);
|
||||
}
|
||||
}
|
||||
|
||||
.step {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
border: 2px solid rgba(var(--color-foreground-raw), 0.2);
|
||||
color: rgba(var(--color-foreground-raw), 0.2);
|
||||
transition: all 0.2s var(--easing-default);
|
||||
|
||||
&.completed {
|
||||
border: 2px solid var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.dash {
|
||||
display: block;
|
||||
width: 50px;
|
||||
height: 6px;
|
||||
content: '';
|
||||
border-radius: 3px;
|
||||
background-color: rgba(var(--color-foreground-raw), 0.1);
|
||||
|
||||
&.completed {
|
||||
background-color: rgba(var(--color-primary-raw), 0.4);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,62 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import AutocompleteInput from '../Autocomplete.svelte/AutocompleteInput.svelte';
|
||||
import type { NaceEntry } from './types';
|
||||
|
||||
export let selectedNace: NaceEntry | undefined = undefined;
|
||||
|
||||
let options: string[] = [];
|
||||
let naceOptions: NaceEntry[] = [];
|
||||
|
||||
onMount(async () => {
|
||||
const naceEntries: NaceEntry[] = await fetch('/nace.json').then((r) => r.json());
|
||||
if (!naceEntries) {
|
||||
return;
|
||||
}
|
||||
naceOptions = naceEntries;
|
||||
|
||||
options = naceEntries.map((entry) => `${entry.code} - ${entry.name}`);
|
||||
});
|
||||
|
||||
const onNaceSelected = (evt: CustomEvent<string | undefined>) => {
|
||||
const nace = evt.detail;
|
||||
if (!nace) {
|
||||
selectedNace = undefined;
|
||||
}
|
||||
|
||||
let code = nace!.split(' ')[0];
|
||||
|
||||
// Find matching entry
|
||||
selectedNace = naceOptions.find((nace) => nace.code === code);
|
||||
};
|
||||
</script>
|
||||
|
||||
<form>
|
||||
<section>
|
||||
<h2>Company Classification</h2>
|
||||
<label>
|
||||
<span>Select NACE code</span>
|
||||
<AutocompleteInput on:select={onNaceSelected} {options} placeholder="NACE Code" />
|
||||
</label>
|
||||
<slot />
|
||||
<br /><br />
|
||||
<br />
|
||||
<div>
|
||||
<h3>What are NACE codes?</h3>
|
||||
<p>
|
||||
NACE codes are used to classify economic activities in the European Union (EU). They are
|
||||
based on the NACE (Nomenclature of Economic Activities) classification system, which was
|
||||
developed by the EU to provide a common framework for the collection and analysis of
|
||||
statistical data related to economic activities. NACE codes are used to identify the type of
|
||||
business or industry in which a company operates, and are typically used for statistical and
|
||||
analytical purposes.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
label > span {
|
||||
padding-left: var(--size-3);
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +0,0 @@
|
||||
export type NaceEntry = {
|
||||
section: string;
|
||||
code: string;
|
||||
name: string;
|
||||
};
|
||||
70
frontend/src/routes/chat/+page.svelte
Normal file
70
frontend/src/routes/chat/+page.svelte
Normal file
@ -0,0 +1,70 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import Card from '$lib/components/Card.svelte';
|
||||
import Container from '$lib/components/Container.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import Input from '$lib/components/Input.svelte';
|
||||
import Liquid from '$lib/components/Liquid.svelte';
|
||||
import Notification from '$lib/components/Notification.svelte';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
let token: string = '';
|
||||
let msg: string | undefined = undefined;
|
||||
let history: string | undefined = undefined;
|
||||
|
||||
onMount(() => {
|
||||
token = localStorage.getItem('token') ?? '';
|
||||
});
|
||||
|
||||
function sendRequest() {
|
||||
if (!msg || msg.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Loading animation!
|
||||
|
||||
jQuery.post("https://" + window.location.hostname + "/api", {
|
||||
history: history,
|
||||
msg: msg,
|
||||
token: token
|
||||
}).done(function (data: any) {
|
||||
history = data;
|
||||
msg = "";
|
||||
});
|
||||
}
|
||||
function keyUp (e: { key: string; keyCode: number; }) {
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
sendRequest();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Container centered>
|
||||
<p>
|
||||
<Card background={false}>
|
||||
<form>
|
||||
<section>
|
||||
<h2>Chatbot</h2>
|
||||
<textarea bind:value={history} disabled />
|
||||
<input id="chatbot-input" type="text" placeholder="Type your message here..." bind:value={msg} on:keyup={keyUp} />
|
||||
<Button on:click={sendRequest}>Send</Button>
|
||||
</section>
|
||||
</form>
|
||||
</Card>
|
||||
</p>
|
||||
</Container>
|
||||
|
||||
<style lang="scss">
|
||||
@import '$lib/style/app.scss';
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--size-5);
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
}
|
||||
</style>
|
||||
@ -6,6 +6,18 @@
|
||||
import Input from '$lib/components/Input.svelte';
|
||||
import Liquid from '$lib/components/Liquid.svelte';
|
||||
import Notification from '$lib/components/Notification.svelte';
|
||||
|
||||
let token: string | undefined = undefined;
|
||||
|
||||
const onLogin = (evt: MouseEvent) => {
|
||||
if (!token) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
return;
|
||||
}
|
||||
localStorage.setItem('token', token ?? '');
|
||||
};
|
||||
</script>
|
||||
|
||||
<Container centered>
|
||||
@ -18,8 +30,8 @@
|
||||
<form>
|
||||
<section>
|
||||
<h2>Login</h2>
|
||||
<input placeholder="Access token" type="password" />
|
||||
<Button primary>Login</Button>
|
||||
<input placeholder="Access token" type="password" bind:value={token} />
|
||||
<a href="/chat" on:click={onLogin}><Button>Login</Button></a>
|
||||
</section>
|
||||
</form>
|
||||
</Card>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user