sync
This commit is contained in:
parent
dc98b0bf37
commit
073082b520
13 changed files with 139 additions and 185 deletions
34
src/App.vue
34
src/App.vue
|
@ -1,26 +1,17 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Paper, Search, Filter } from '@/types'
|
import { state, fetchPapers, getTopics } from '@/stores'
|
||||||
import { state, papersFetch } from '@/store'
|
|
||||||
import MainMenu from '@/components/MainMenu.vue'
|
import MainMenu from '@/components/MainMenu.vue'
|
||||||
import SearchBar from '@/components/SearchBar.vue'
|
import SearchBar from '@/components/SearchBar.vue'
|
||||||
import FilterView from './components/papers/FilterView.vue'
|
/* import FooterMenu from '@/components/FooterMenu.vue' */
|
||||||
import FooterMenu from '@/components/FooterMenu.vue'
|
|
||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
const applicationName: string = 'Stadtratmonitor'
|
const applicationName: string = 'Stadtratmonitor'
|
||||||
const cityName: string = 'Leipzig'
|
const cityName: string = 'Leipzig'
|
||||||
let search: Search = {
|
|
||||||
value: '',
|
onMounted (async () => {
|
||||||
type: '',
|
await fetchPapers()
|
||||||
}
|
getTopics()
|
||||||
let filter: Filter = {
|
})
|
||||||
type: {
|
|
||||||
key: '',
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
originator: '',
|
|
||||||
}
|
|
||||||
onMounted (() => papersFetch() )
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -34,18 +25,11 @@ onMounted (() => papersFetch() )
|
||||||
</h1>
|
</h1>
|
||||||
<MainMenu />
|
<MainMenu />
|
||||||
</div>
|
</div>
|
||||||
<SearchBar
|
<SearchBar />
|
||||||
@searchSubmit="(type) => search.type = type"
|
|
||||||
@searchQuery="(query) => search.value = query"
|
|
||||||
/>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="flex flex-row max-w-5xl m-auto">
|
<main class="flex flex-row max-w-5xl m-auto">
|
||||||
{{ state.topics }}
|
<RouterView>
|
||||||
<RouterView
|
|
||||||
:search="search"
|
|
||||||
:filter="filter"
|
|
||||||
>
|
|
||||||
</RouterView>
|
</RouterView>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
export default {
|
const entries = {
|
||||||
data() {
|
0: {
|
||||||
return {
|
name: 'Impressum',
|
||||||
menuEntries: this.footerMenuEntries,
|
uri: '/impressum',
|
||||||
}
|
},
|
||||||
|
1: {
|
||||||
|
name: 'Über diese Seite',
|
||||||
|
uri: '/über',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -11,11 +14,11 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<ul class="flex flex-row place-content-center">
|
<ul class="flex flex-row place-content-center">
|
||||||
<li
|
<li
|
||||||
v-for="{entry, i} of menuEntries"
|
|
||||||
:key="i"
|
|
||||||
class="p-2 place-content-center"
|
class="p-2 place-content-center"
|
||||||
|
v-for="{entry, i} of entries"
|
||||||
|
:key="i"
|
||||||
>
|
>
|
||||||
<a :href="entry.uri">{{ entry.name }}</a>
|
<RouterLink :to="entry.uri">{{ entry.name }}</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
|
@ -1,10 +1,17 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
export default {
|
const entries = {
|
||||||
data() {
|
0: {
|
||||||
return {
|
name: 'Themen',
|
||||||
entries: this.mainMenuEntries
|
uri: '/themen',
|
||||||
}
|
},
|
||||||
}
|
1: {
|
||||||
|
name: 'Karte',
|
||||||
|
uri: '/karte',
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
name: 'Dokumentation',
|
||||||
|
uri: '/doku',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,11 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
export default {
|
import { updateSearch } from '@/stores';
|
||||||
updated() {
|
import { onUpdated, ref } from 'vue';
|
||||||
this.$emit('searchQuery', this.search.value)
|
|
||||||
},
|
let searchValue = ref('')
|
||||||
data() {
|
let searchType = ref('')
|
||||||
return {
|
function submit(type: string) { searchType = ref(type) }
|
||||||
search: {
|
onUpdated(() => updateSearch(searchValue, searchType))
|
||||||
value: '',
|
|
||||||
type: '',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
submit(type: string) {
|
|
||||||
this.search.type = type
|
|
||||||
this.$emit('searchSubmit', this.search.type)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -27,7 +15,7 @@ export default {
|
||||||
class="p-6 grow bg-transparent placeholder:text-text-300 dark:placeholder:text-text-700 text-2xl text-center focus-visible:outline focus-visible:outline-current"
|
class="p-6 grow bg-transparent placeholder:text-text-300 dark:placeholder:text-text-700 text-2xl text-center focus-visible:outline focus-visible:outline-current"
|
||||||
type="search"
|
type="search"
|
||||||
placeholder="z. B. Thema, Name, VII-EF-08640, …"
|
placeholder="z. B. Thema, Name, VII-EF-08640, …"
|
||||||
v-model="search.value"
|
v-model="searchValue"
|
||||||
@keyup.alt.enter.exact="submit('assist')"
|
@keyup.alt.enter.exact="submit('assist')"
|
||||||
@keyup.enter.exact="submit('filter')"
|
@keyup.enter.exact="submit('filter')"
|
||||||
/>
|
/>
|
||||||
|
@ -48,4 +36,5 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{{ searchValue + searchType }}
|
||||||
</template>
|
</template>
|
|
@ -1,13 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Paper, Filter } from '@/types'
|
import type { Paper, Filter } from '@/types'
|
||||||
|
import { state, updateFilter } from '@/stores';
|
||||||
import { computed, onUpdated } from 'vue';
|
import { computed, onUpdated } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
papers: Array<Paper>,
|
|
||||||
}>()
|
|
||||||
const paperTypes = [
|
const paperTypes = [
|
||||||
{
|
{
|
||||||
value: 'Anfrage',
|
value: 'Anfrage',
|
||||||
|
@ -52,13 +47,13 @@ const filter: Filter = {
|
||||||
originator: '',
|
originator: '',
|
||||||
}
|
}
|
||||||
const paperOriginators = computed(() => {
|
const paperOriginators = computed(() => {
|
||||||
return [...new Set(props.papers?.map((paper: Paper) => paper.originator))].sort()
|
return [...new Set(state.papers?.map((paper: Paper) => paper.originator))].sort()
|
||||||
})
|
})
|
||||||
/* const paperType = computed(() => {
|
/* const paperType = computed(() => {
|
||||||
return paperTypes.filter((type) => type.key == )
|
return paperTypes.filter((type) => type.key == )
|
||||||
}) */
|
}) */
|
||||||
onUpdated(() => {
|
onUpdated(() => {
|
||||||
route.$emit('filter', filter)
|
updateFilter(filter.type.key, filter.type.value, filter.originator)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,51 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Topic, Search, Filter } from '@/types'
|
import { state } from '@/stores'
|
||||||
import { computed } from 'vue';
|
import type { Topic } from '@/types';
|
||||||
|
import { computed, onMounted, onUpdated } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
topics: Array<Topic>,
|
|
||||||
search: Search,
|
|
||||||
filter: Filter,
|
|
||||||
}>()
|
|
||||||
const filteredData = computed(() => {
|
const filteredData = computed(() => {
|
||||||
//const searchValue: string = search['value'];
|
const searchValue: string = state.search.value;
|
||||||
let filteredTopics = props.topics
|
let filteredTopics = state.topics
|
||||||
/* if (searchValue !== '') {
|
if (state.search.value !== '') {
|
||||||
filteredTopics = this.topics?.filter((topic: Object) => {
|
filteredTopics = state.topics?.filter((topic: Topic) => {
|
||||||
return topic.papers?.filter().name.toLowerCase().includes(searchValue.toLowerCase()) || paper.content.toLowerCase().includes(searchValue.toLowerCase()) || paper.reference.toLowerCase().includes(searchValue.toLowerCase())
|
return topic.papers?.filter((paper) => {
|
||||||
}) as Array<Object>
|
paper.name.toLowerCase().includes(searchValue.toLowerCase()) /* || paper.content.toLowerCase().includes(searchValue.toLowerCase()) || paper.reference.toLowerCase().includes(searchValue.toLowerCase()) */
|
||||||
} */
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
/* if (this.filter?.type !== '') {
|
/* if (this.filter?.type !== '') {
|
||||||
filteredTopics = filteredTopics.filter((topic: any) => {
|
filteredTopics = filteredTopics.filter((topic: any) => {
|
||||||
return topic.reference.includes(this.filter?.type.key) && topic.paper_type.includes(this.filter?.type.value)
|
return topic.reference.includes(this.filter?.type.key) && topic.paper_type.includes(this.filter?.type.value)
|
||||||
})
|
})
|
||||||
}
|
} */
|
||||||
if (this.filter?.originator !== '') {
|
/* if (this.filter?.originator !== '') {
|
||||||
filteredTopics = filteredTopics.filter((topic: any) => {
|
filteredTopics = filteredTopics.filter((topic: any) => {
|
||||||
return topic.originator.includes(this.filter?.originator)
|
return topic.originator.includes(this.filter?.originator)
|
||||||
})
|
})
|
||||||
} */
|
} */
|
||||||
return filteredTopics
|
return filteredTopics
|
||||||
})
|
})
|
||||||
const filteredDataLength = computed(() => {
|
let filteredDataLength: number = 0
|
||||||
if (filteredData.value !== undefined) {
|
|
||||||
return Object.keys(filteredData).length
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function date(paperDate: Date) {
|
function objectLength(data: any) {
|
||||||
|
return Object.keys(data).length
|
||||||
|
}
|
||||||
|
function date(paperDate: string) {
|
||||||
const date = new Date(paperDate)
|
const date = new Date(paperDate)
|
||||||
return new Intl.DateTimeFormat('de-DE', { dateStyle: 'full' }).format(date)
|
return new Intl.DateTimeFormat('de-DE', { dateStyle: 'full' }).format(date)
|
||||||
}
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
filteredDataLength = objectLength(filteredData)
|
||||||
|
})
|
||||||
|
onUpdated(() => {
|
||||||
|
filteredDataLength = objectLength(filteredData)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
{{ state.search.value }}
|
||||||
<ul
|
<ul
|
||||||
v-if="filteredDataLength"
|
v-if="filteredDataLength > 0"
|
||||||
class="w-full grid grid-flow-row gap-2 my-2"
|
class="w-full grid grid-flow-row gap-2 my-2"
|
||||||
>
|
>
|
||||||
<p>Wir konnten {{ filteredDataLength }} Einträge finden</p>
|
<p>Wir konnten {{ filteredDataLength }} Einträge finden</p>
|
||||||
|
@ -55,15 +57,15 @@ function date(paperDate: Date) {
|
||||||
class="p-4 rounded-lg bg-background-100 dark:bg-background-900"
|
class="p-4 rounded-lg bg-background-100 dark:bg-background-900"
|
||||||
>
|
>
|
||||||
<h4 class="text-xl">{{ topic.papers[0].name }}</h4>
|
<h4 class="text-xl">{{ topic.papers[0].name }}</h4>
|
||||||
<!-- <p>{{ date(topic.papers[0].published_at) }}: <a :href="topic.papers[0].url" class="text-secondary-button-500">{{ paper.paper_type}} von {{ paper.originator }}</a></p> -->
|
<p>{{ date(topic.papers[0].published_at) }}: <a :href="topic.papers[0].url" class="text-secondary-button-500">{{ topic.papers[0].paper_type}} von {{ topic.papers[0].originator }}</a></p>
|
||||||
</article>
|
</article>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- <p
|
<p
|
||||||
class="flex place-content-center my-60 text-lg"
|
class="flex place-content-center my-60 text-lg"
|
||||||
v-else-if="topics?.length"
|
v-else-if="state.topics?.length"
|
||||||
>Für dieses Anfrage liegen uns keine Ergebnisse vor.
|
>Für dieses Anfrage liegen uns keine Ergebnisse vor.
|
||||||
</p> -->
|
</p>
|
||||||
<p
|
<p
|
||||||
class="flex place-content-center my-60 text-lg"
|
class="flex place-content-center my-60 text-lg"
|
||||||
v-else
|
v-else
|
||||||
|
|
28
src/main.ts
28
src/main.ts
|
@ -6,32 +6,4 @@ import router from '@/router'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.config.globalProperties = {
|
|
||||||
...app.config.globalProperties,
|
|
||||||
mainMenuEntries: {
|
|
||||||
0: {
|
|
||||||
name: 'Themen',
|
|
||||||
uri: '/themen',
|
|
||||||
},
|
|
||||||
1: {
|
|
||||||
name: 'Karte',
|
|
||||||
uri: '/karte'
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
name: 'Dokumentation',
|
|
||||||
uri: '/doku'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
footerMenuEntries: {
|
|
||||||
0: {
|
|
||||||
name: 'Impressum',
|
|
||||||
uri: '/impressum',
|
|
||||||
},
|
|
||||||
1: {
|
|
||||||
name: 'Über diese Seite',
|
|
||||||
uri: '/über'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app.use(router).mount('#app')
|
app.use(router).mount('#app')
|
||||||
|
|
|
@ -10,7 +10,6 @@ const routes: Array<any> = [
|
||||||
path: '/themen/',
|
path: '/themen/',
|
||||||
name: 'topics',
|
name: 'topics',
|
||||||
component: ()=>import("@/views/TheTopics.vue"),
|
component: ()=>import("@/views/TheTopics.vue"),
|
||||||
props: { topics: true, search: true },
|
|
||||||
}, {
|
}, {
|
||||||
path: '/themen/:reference',
|
path: '/themen/:reference',
|
||||||
name: 'topics.show',
|
name: 'topics.show',
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { reactive } from 'vue'
|
|
||||||
import type { Paper, Topic } from '@/types'
|
|
||||||
|
|
||||||
const apiUrl: URL = new URL(
|
|
||||||
'https://raw.githubusercontent.com/CodeforLeipzig/stadtratmonitor/master/input.json'
|
|
||||||
)
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
papers: Paper[]
|
|
||||||
topics: Topic[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const state: State = reactive({ papers: [], topics: [] })
|
|
||||||
|
|
||||||
export async function papersFetch() {
|
|
||||||
const papersData: Paper[] = await (await fetch(apiUrl)).json()
|
|
||||||
const topicReferences = await [...new Set(papersData?.map((paper) => paper.reference))]
|
|
||||||
state.papers = papersData
|
|
||||||
state.topics = await topicReferences.map((reference) => {
|
|
||||||
return {
|
|
||||||
reference: reference,
|
|
||||||
papers: papersData?.filter((paper: Paper) => paper.reference === reference)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
49
src/stores/index.ts
Normal file
49
src/stores/index.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { reactive, type Ref, ref } from 'vue'
|
||||||
|
import type { Filter, Paper, Search, Topic } from '@/types'
|
||||||
|
|
||||||
|
const apiUrl: URL = new URL('https://raw.githubusercontent.com/CodeforLeipzig/stadtratmonitor/master/input.json')
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
papers: Paper[],
|
||||||
|
topics: Topic[],
|
||||||
|
search: Search,
|
||||||
|
filter: Filter[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const state: State = reactive({
|
||||||
|
papers: [],
|
||||||
|
topics: [],
|
||||||
|
search: { value: ref(), type: ref() },
|
||||||
|
filter: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function fetchPapers() {
|
||||||
|
state.papers = await (await fetch(apiUrl)).json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTopics() {
|
||||||
|
const topicReferences = [...new Set(state.papers?.map((paper) => paper.reference))]
|
||||||
|
state.topics = topicReferences.map((reference) => {
|
||||||
|
return {
|
||||||
|
reference: reference,
|
||||||
|
papers: state.papers?.filter((paper: Paper) => paper.reference === reference)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateSearch(searchValue: Ref, searchType: Ref) {
|
||||||
|
state.search = {
|
||||||
|
value: searchValue,
|
||||||
|
type: searchType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateFilter(typeKey: string, typeValue: string, originator: string) {
|
||||||
|
state.filter = [{
|
||||||
|
type: {
|
||||||
|
key: typeKey,
|
||||||
|
value: typeValue,
|
||||||
|
},
|
||||||
|
originator: originator,
|
||||||
|
}]
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
|
||||||
export interface Topic {
|
export interface Topic {
|
||||||
reference: string,
|
reference: string,
|
||||||
papers: Array<Paper>,
|
papers: Array<Paper>,
|
||||||
|
@ -16,8 +18,8 @@ export interface Paper {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Search {
|
export interface Search {
|
||||||
value: string,
|
value: Ref,
|
||||||
type: string,
|
type: Ref,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Filter {
|
export interface Filter {
|
|
@ -1,31 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Topic, Paper, Search, Filter } from '@/types'
|
|
||||||
import { papers } from '@/store'
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import FilterSidebar from '@/components/papers/FilterSidebar.vue'
|
import FilterSidebar from '@/components/papers/FilterSidebar.vue'
|
||||||
import TopicList from '@/components/papers/TopicList.vue'
|
import TopicList from '@/components/papers/TopicList.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
search: Search,
|
|
||||||
filter: Filter,
|
|
||||||
}>()
|
|
||||||
const topics = computed(() => {
|
|
||||||
const topicReferences = [...new Set(papers.papers?.map((paper: Paper) => paper.reference))]
|
|
||||||
return topicReferences.map( (reference: string) => {
|
|
||||||
return {
|
|
||||||
'reference': reference,
|
|
||||||
'papers': papers.papers?.filter( (paper: Paper) => paper.reference === reference),
|
|
||||||
}
|
|
||||||
}) as Array<Topic>
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FilterSidebar
|
<FilterSidebar />
|
||||||
@filter="(filter: Filter) => filter = filter"
|
<TopicList />
|
||||||
/>
|
|
||||||
<TopicList
|
|
||||||
:searchProp="search"
|
|
||||||
:filterProp="filter"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
|
@ -1,19 +1,18 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
/* import { computed } from 'vue'
|
||||||
import type { Paper } from '@/types'
|
import type { Paper } from '@/types'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
papers: Array<Paper>,
|
papers: Array<Paper>,
|
||||||
})
|
})
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const topicId = computed(() => {
|
const topicId = computed(() => {
|
||||||
return route.params.id
|
return route.params.id
|
||||||
})
|
})
|
||||||
const topic = computed(() => {
|
const topic = computed(() => {
|
||||||
props.papers?.find( (paper: any) => paper.reference == topicId )
|
return props.papers?.find( (paper: any) => paper.reference == topicId.value )
|
||||||
})
|
}) */
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
Loading…
Reference in a new issue