[#44] Added frontend source code
This commit is contained in:
parent
659632eba5
commit
687632a704
19 changed files with 341 additions and 0 deletions
1
web/.env
Normal file
1
web/.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
VITE_ENDPOINT=http://localhost:8000
|
||||
1
web/.env.production
Normal file
1
web/.env.production
Normal file
|
|
@ -0,0 +1 @@
|
|||
VITE_ENDPOINT=https://fej.roosens.me/api
|
||||
5
web/.gitignore
vendored
Normal file
5
web/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
27
web/README.md
Normal file
27
web/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Vue 3 + Typescript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and Typescript in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur). Make sure to enable `vetur.experimental.templateInterpolationService` in settings!
|
||||
|
||||
### If Using `<script setup>`
|
||||
|
||||
[`<script setup>`](https://github.com/vuejs/rfcs/pull/227) is a feature that is currently in RFC stage. To get proper IDE support for the syntax, use [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) instead of Vetur (and disable Vetur).
|
||||
|
||||
## Type Support For `.vue` Imports in TS
|
||||
|
||||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can use the following:
|
||||
|
||||
### If Using Volar
|
||||
|
||||
Run `Volar: Switch TS Plugin on/off` from VSCode command palette.
|
||||
|
||||
### If Using Vetur
|
||||
|
||||
1. Install and add `@vuedx/typescript-plugin-vue` to the [plugins section](https://www.typescriptlang.org/tsconfig#plugins) in `tsconfig.json`
|
||||
2. Delete `src/shims-vue.d.ts` as it is no longer needed to provide module info to Typescript
|
||||
3. Open `src/main.ts` in VSCode
|
||||
4. Open the VSCode command palette
|
||||
5. Search and run "Select TypeScript version" -> "Use workspace version"
|
||||
13
web/index.html
Normal file
13
web/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
20
web/package.json
Normal file
20
web/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "fej-frontend",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.0.5",
|
||||
"vue-router": "^4.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^1.2.1",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
"typescript": "^4.1.3",
|
||||
"vite": "^2.2.1",
|
||||
"vue-tsc": "^0.0.25"
|
||||
}
|
||||
}
|
||||
BIN
web/public/favicon.ico
Normal file
BIN
web/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
27
web/src/App.vue
Normal file
27
web/src/App.vue
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<Nav />
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import Nav from './components/Nav.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
components: {
|
||||
Nav,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
</style>
|
||||
32
web/src/api/ivago.ts
Normal file
32
web/src/api/ivago.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
export class Street {
|
||||
name: string;
|
||||
city: string;
|
||||
|
||||
constructor(name: string, city: string) {
|
||||
this.name = name;
|
||||
this.city = city;
|
||||
}
|
||||
}
|
||||
|
||||
export class Ivago {
|
||||
base_url: string;
|
||||
|
||||
constructor(url: string) {
|
||||
this.base_url = url;
|
||||
}
|
||||
|
||||
async search(search_term: string): Promise<Street[]> {
|
||||
var r = await fetch(`${this.base_url}/ivago/search?` + new URLSearchParams({
|
||||
q: search_term,
|
||||
}));
|
||||
|
||||
if (!r.ok) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
var json = await r.json();
|
||||
|
||||
return json.map((o: {name: string, city: string}) => new Street(o.name, o.city));
|
||||
}
|
||||
}
|
||||
|
||||
BIN
web/src/assets/logo.png
Normal file
BIN
web/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
51
web/src/components/HelloWorld.vue
Normal file
51
web/src/components/HelloWorld.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<h1>Fej Frontend</h1>
|
||||
|
||||
<input id="street" type="text" placeholder="street" v-model="street" />
|
||||
<button :onClick="onSubmit">Search</button>
|
||||
|
||||
<ul>
|
||||
<li v-for="res in results">{{res.name}} ({{res.city}})</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { Street, Ivago } from '../api/ivago'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HelloWorld',
|
||||
data() {
|
||||
return {
|
||||
street: "",
|
||||
results: [] as Street[],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
new Ivago(import.meta.env.VITE_ENDPOINT as string).search(this.$data.street)
|
||||
.then(res => {
|
||||
this.$data.results = res;
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #eee;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
color: #304455;
|
||||
}
|
||||
</style>
|
||||
13
web/src/components/Home.vue
Normal file
13
web/src/components/Home.vue
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<h1>Fej</h1>
|
||||
<p>Welcome to Fej, my frontend/backend combo.</p>
|
||||
<p>If you can see this, the cicd worked!</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Home',
|
||||
})
|
||||
</script>
|
||||
52
web/src/components/Ivago.vue
Normal file
52
web/src/components/Ivago.vue
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<h1>Ivago</h1>
|
||||
<input v-model="query" v-on:keyup.enter="search" type="text" placeholder="Street..." />
|
||||
<div id="scroll-list">
|
||||
<ul v-if="msg === ''">
|
||||
<li v-for="item in items">{{ item.name }}</li>
|
||||
</ul>
|
||||
<p v-else>{{ msg }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { Street, Ivago } from '../api/ivago'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Ivago',
|
||||
data() {
|
||||
return {
|
||||
items: [] as Street[],
|
||||
msg: "",
|
||||
query: "",
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
search() {
|
||||
this.items = []
|
||||
this.msg = "Loading..."
|
||||
|
||||
if (this.query === "") {
|
||||
this.msg = ""
|
||||
return
|
||||
}
|
||||
|
||||
new Ivago(import.meta.env.VITE_ENDPOINT as string)
|
||||
.search(this.query)
|
||||
.then((res: Street[]) => {
|
||||
this.items = res
|
||||
this.msg = ""
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#scroll-list {
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
43
web/src/components/Nav.vue
Normal file
43
web/src/components/Nav.vue
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<div id="menu-wrapper">
|
||||
<nav id="menu">
|
||||
<router-link to="/">Home</router-link>
|
||||
<router-link to="/ivago">Ivago</router-link>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Nav',
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#menu-wrapper {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 150px;
|
||||
background-color: #242624;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#menu {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
#menu > a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
color: #5f635f;
|
||||
text-decoration: none;
|
||||
}
|
||||
#menu > a:hover {
|
||||
color: #c2ccc1;
|
||||
}
|
||||
</style>
|
||||
8
web/src/main.ts
Normal file
8
web/src/main.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
21
web/src/router.ts
Normal file
21
web/src/router.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { createWebHistory, createRouter } from "vue-router";
|
||||
import Home from './components/Home.vue';
|
||||
import Ivago from './components/Ivago.vue';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
component: Home,
|
||||
},
|
||||
{
|
||||
path: "/ivago",
|
||||
component: Ivago,
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
5
web/src/shims-vue.d.ts
vendored
Normal file
5
web/src/shims-vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
15
web/tsconfig.json
Normal file
15
web/tsconfig.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"],
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
||||
7
web/vite.config.ts
Normal file
7
web/vite.config.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()]
|
||||
})
|
||||
Reference in a new issue