Project list and starting editing
This commit is contained in:
parent
a3d0b3b5bd
commit
aba56bb034
27
app.vue
27
app.vue
|
|
@ -9,6 +9,15 @@ function hideLeftPanel(_: Event): void {
|
|||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { id: project, home, get } = useProject();
|
||||
|
||||
if(project.value !== 0 && home.value === null)
|
||||
{
|
||||
const { data: useless } = await useAsyncData(`project:get:${project}`, get);
|
||||
}
|
||||
|
||||
const toggled = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
icon = document.querySelector('.site-nav-bar .clickable-icon');
|
||||
icon?.removeEventListener('click', toggleLeftPanel);
|
||||
|
|
@ -30,12 +39,18 @@ onMounted(() => {
|
|||
</svg>
|
||||
</div>
|
||||
<div class="gapx-3 flex align-stretch">
|
||||
<NuxtLink @click="hideLeftPanel" class="site-nav-bar-text" aria-label="Accueil" to="/">
|
||||
<NuxtLink @click="hideLeftPanel" class="site-nav-bar-text" aria-label="Accueil" :to="{ path: '/', force: true }">
|
||||
<ThemeIcon icon="logo" :width=40 :height=40 />
|
||||
</NuxtLink>
|
||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Projets" to="/explorer"
|
||||
:class="{'mod-active': $route.path.startsWith('/explorer')}">Projets</NuxtLink>
|
||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Outils" to="/tools"
|
||||
<div class="site-nav-bar-text mobile-hidden">
|
||||
<NuxtLink aria-label="Projet" :to="{ path: project === 0 ? `/explorer` : `/explorer/${project}${home}`, force: true }"
|
||||
:class="{'mod-active': $route.path.startsWith('/explorer')}">Projet</NuxtLink>
|
||||
<div class="arrow-down" :class="{active: toggled}" @click="toggled = !toggled"></div>
|
||||
<div class="arrow-group" @click="toggled = false">
|
||||
<NuxtLink class="arrow-group-item" aria-label="Projets" :to="{ path: '/explorer', force: true }">Liste des projets</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Outils" :to="{ path: '/tools', force: true }"
|
||||
:class="{'mod-active': $route.path.startsWith('/tools')}">Outils</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -44,7 +59,7 @@ onMounted(() => {
|
|||
</div>
|
||||
<div class="ps-1 gapx-1 flex align-center">
|
||||
<ThemeSwitch class="mobile-hidden" />
|
||||
<NuxtLink class="site-login" to="/user/profile">
|
||||
<NuxtLink class="site-login" :to="{ path: '/user/profile', force: true }">
|
||||
<ThemeIcon icon="user" :width=32 :height=32 />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
|
@ -55,7 +70,7 @@ onMounted(() => {
|
|||
</div>
|
||||
<div class="site-footer">
|
||||
<p>Copyright Peaceultime - 2024</p>
|
||||
<NuxtLink href="/third-party">Applications tierces et crédits</NuxtLink>
|
||||
<NuxtLink :to="{ path: '/third-party', force: true }">Applications tierces et crédits</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -286,6 +286,88 @@ html.light-mode .light-block {
|
|||
padding: 0 4px;
|
||||
border-radius: 4px;
|
||||
font-size: var(--font-smaller);
|
||||
/* font-style: italic; */
|
||||
font-variant: all-petite-caps;
|
||||
}
|
||||
|
||||
.suggestion-subtext {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: var(--font-smaller);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
border: 2px solid var(--background-modifier-border-focus);
|
||||
border-top-color: transparent;
|
||||
border-left-color: transparent;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
transform: rotate(45deg);
|
||||
position: relative;
|
||||
left: 8px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.projects-container {
|
||||
flex: 1 1;
|
||||
padding: 2em 4em;
|
||||
}
|
||||
|
||||
.project-list {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.project-item {
|
||||
margin: 1em;
|
||||
padding: .5em;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.project-title {
|
||||
font-size: var(--font-ui-large);
|
||||
font-weight: var(--font-bold);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.project-user {
|
||||
font-size: var(--font-small);
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.project-pages {
|
||||
display: inline;
|
||||
float: inline-end;
|
||||
font-size: var(--font-small);
|
||||
}
|
||||
|
||||
.arrow-group {
|
||||
position: absolute;
|
||||
left: -1em;
|
||||
top: calc(100% + 7px);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
background-color: var(--background-primary);
|
||||
z-index: 99;
|
||||
text-wrap: nowrap;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.arrow-down.active + .arrow-group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.arrow-group .arrow-group-item {
|
||||
padding: 8px 1em;
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--text-normal);
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.arrow-group .arrow-group-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.arrow-group .arrow-group-item:hover {
|
||||
background-color: var(--background-primary-alt);
|
||||
}
|
||||
|
|
@ -2636,7 +2636,7 @@ body:not(.native-scrollbars) * {
|
|||
}
|
||||
|
||||
.suggestion-item {
|
||||
cursor: var(--cursor);
|
||||
cursor: pointer;
|
||||
padding: var(--size-2-3) var(--size-4-3);
|
||||
padding-left: 12px;
|
||||
white-space: pre-wrap;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,4 @@ const afterText = computed(() => {
|
|||
const pos = props.text.toLowerCase().indexOf(props.matched.toLowerCase()) + props.matched.length;
|
||||
return props.text.substring(pos);
|
||||
})
|
||||
|
||||
console.log(props, beforeText.value, afterText.value);
|
||||
</script>
|
||||
|
|
@ -5,14 +5,27 @@ function hideLeftPanel(_: Event)
|
|||
}
|
||||
|
||||
const route = useRoute();
|
||||
const showing = ref(false);
|
||||
|
||||
const project = parseInt(Array.isArray(route.params.projectId) ? '' : route.params.projectId);
|
||||
const project = computed(() => parseInt(Array.isArray(route.params.projectId) ? '0' : route.params.projectId));
|
||||
|
||||
const { data: navigation } = await useFetch(() => isNaN(project) ? '' : `/api/project/${project}/navigation`);
|
||||
const { data: navigation, execute, status, error } = await useFetch(() => `/api/project/${project.value}/navigation`, {
|
||||
immediate: false,
|
||||
});
|
||||
|
||||
if(route.params.projectId && project.value !== 0)
|
||||
{
|
||||
showing.value = true;
|
||||
execute();
|
||||
}
|
||||
else
|
||||
{
|
||||
showing.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="site-body-left-column">
|
||||
<div :class="{'desktop-hidden': !showing}" class="site-body-left-column">
|
||||
<div class="site-body-left-column-inner">
|
||||
<div class="nav-view-outer">
|
||||
<div class="nav-view">
|
||||
|
|
|
|||
|
|
@ -1,3 +1,32 @@
|
|||
<template>
|
||||
<slot
|
||||
:data="data?.data"
|
||||
:body="data?.body"
|
||||
:toc="data?.toc"
|
||||
:excerpt="data?.excerpt"
|
||||
:error="error"
|
||||
>
|
||||
<MDCRenderer
|
||||
v-if="body"
|
||||
:tag="tag"
|
||||
:class="props.class"
|
||||
:body="body"
|
||||
:data="data?.data"
|
||||
:unwrap="props.unwrap"
|
||||
:components="{
|
||||
a: ProseA,
|
||||
h1: ProseH1,
|
||||
h2: ProseH2,
|
||||
h3: ProseH3,
|
||||
h4: ProseH4,
|
||||
h5: ProseH5,
|
||||
h6: ProseH6,
|
||||
blockquote: ProseBlockquote,
|
||||
}"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ProseA from "~/components/content/prose/ProseA.vue";
|
||||
import ProseH1 from "~/components/content/prose/ProseH1.vue";
|
||||
|
|
@ -7,27 +36,54 @@ import ProseH4 from "~/components/content/prose/ProseH4.vue";
|
|||
import ProseH5 from "~/components/content/prose/ProseH5.vue";
|
||||
import ProseH6 from "~/components/content/prose/ProseH6.vue";
|
||||
import ProseBlockquote from "~/components/content/prose/ProseBlockquote.vue";
|
||||
const props = defineProps<{
|
||||
content: string
|
||||
}>();
|
||||
const parser = useMarkdown();
|
||||
const ast = await parser(props.content);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Suspense>
|
||||
<template #fallback>
|
||||
<div class="loading"></div>
|
||||
</template>
|
||||
<MDCRenderer :body="ast?.body" :data="ast?.data" :components="{
|
||||
a: ProseA,
|
||||
h1: ProseH1,
|
||||
h2: ProseH2,
|
||||
h3: ProseH3,
|
||||
h4: ProseH4,
|
||||
h5: ProseH5,
|
||||
h6: ProseH6,
|
||||
blockquote: ProseBlockquote,
|
||||
}" />
|
||||
</Suspense>
|
||||
</template>
|
||||
import { hash } from 'ohash'
|
||||
import { useAsyncData } from 'nuxt/app'
|
||||
import { watch, computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
tag: {
|
||||
type: [String, Boolean],
|
||||
default: 'div'
|
||||
},
|
||||
content: {
|
||||
type: [String, Object],
|
||||
required: true
|
||||
},
|
||||
excerpt: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
class: {
|
||||
type: [String, Array, Object],
|
||||
default: ''
|
||||
},
|
||||
unwrap: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const model = defineModel<number>({
|
||||
default: 0,
|
||||
});
|
||||
|
||||
const parser = useMarkdown();
|
||||
const key = computed(() => hash(props.content))
|
||||
|
||||
const { data, refresh, error } = await useAsyncData(key.value, async () => {
|
||||
const timer = performance.now();
|
||||
if (typeof props.content !== 'string') {
|
||||
model.value = performance.now() - timer;
|
||||
return props.content
|
||||
}
|
||||
const result = await parser(props.content);
|
||||
model.value = performance.now() - timer;
|
||||
return result;
|
||||
})
|
||||
|
||||
const body = computed(() => props.excerpt ? data.value?.excerpt : data.value?.body)
|
||||
|
||||
watch(() => props.content, () => {
|
||||
refresh()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -54,8 +54,10 @@ async function debounced()
|
|||
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
||||
@mousedown.prevent="navigateTo(`/explorer/${result.id}${result.home}`); input = ''">
|
||||
<div class="suggestion-content">
|
||||
<div class="suggestion-title">
|
||||
<BoldContent :text="result.name" :matched="input" />
|
||||
<BoldContent class="suggestion-title" :text="result.name" :matched="input" />
|
||||
<div class="suggestion-subtext">
|
||||
<div class="suggestion-text">{{ result.username }}</div>
|
||||
<div class="suggestion-text">{{ result.pages }} pages</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -64,10 +66,10 @@ async function debounced()
|
|||
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
||||
@mousedown.prevent="navigateTo(`/explorer/${result.project}${result.path}`); input = ''">
|
||||
<div class="suggestion-content">
|
||||
<div class="suggestion-title">
|
||||
<ProseA :href="result.path" :project="result.project">
|
||||
<BoldContent :text="result.title" :matched="input" />
|
||||
</ProseA>
|
||||
<BoldContent class="suggestion-title" :text="result.title" :matched="input" />
|
||||
<div class="suggestion-subtext">
|
||||
<div class="suggestion-text">{{ result.username }}</div>
|
||||
<div class="suggestion-text">{{ result.comments }} commentaires</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -76,11 +78,7 @@ async function debounced()
|
|||
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
||||
@mousedown.prevent="navigateTo(`/user/${result.id}`); input = ''">
|
||||
<div class="suggestion-content">
|
||||
<div class="suggestion-title">
|
||||
<div>
|
||||
<BoldContent :text="result.username" :matched="input" />
|
||||
</div>
|
||||
</div>
|
||||
<BoldContent class="suggestion-title" :text="result.username" :matched="input" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="suggestion-empty"
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
<div v-else class="loading"></div>
|
||||
</div>
|
||||
</Teleport>
|
||||
<NuxtLink :to="path" :class="class" noPrefetch @mouseenter="(e) => showPreview(e, true)" @mouseleave="hidePreview">
|
||||
<NuxtLink :to="{ path, force: true }" :class="class" noPrefetch @mouseenter="(e) => showPreview(e, true)" @mouseleave="hidePreview">
|
||||
<slot v-bind="$attrs"></slot>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
|
@ -74,22 +74,30 @@ if (parseURL(props.href).protocol !== undefined)
|
|||
let id = ref(props.project);
|
||||
if(id.value === undefined)
|
||||
{
|
||||
id = useProject().id;
|
||||
id.value = useProject().id.value;
|
||||
}
|
||||
|
||||
const { data: page, status, error, execute } = await useLazyFetch(`/api/project/${id.value}/file`, {
|
||||
query: {
|
||||
search: "%" + parseURL(props.href).pathname,
|
||||
}
|
||||
},
|
||||
immediate: false,
|
||||
dedupe: 'defer',
|
||||
});
|
||||
|
||||
if(external.value)
|
||||
if(!external.value)
|
||||
{
|
||||
execute();
|
||||
await execute();
|
||||
}
|
||||
|
||||
if(status.value === 'error')
|
||||
{
|
||||
console.error(error.value);
|
||||
}
|
||||
|
||||
if (page.value && page.value[0])
|
||||
{
|
||||
path.value = `/explorer/${id.value}${page.value[0].path}`;
|
||||
console.log(path.value);
|
||||
}
|
||||
</script>
|
||||
|
|
@ -51,7 +51,7 @@ function hideLeftPanel(_: Event)
|
|||
<NavigationLink v-if="hasChildren" v-for="l of link.children" :link="l" :project="project" />
|
||||
</div>
|
||||
</template>
|
||||
<NuxtLink @click="hideLeftPanel" v-else class="tree-item-self" :to="`/explorer/${project}${link.path}`"
|
||||
<NuxtLink @click="hideLeftPanel" v-else class="tree-item-self" :to="{ path: `/explorer/${project}${link.path}`, force: true }"
|
||||
:active-class="'mod-active'" :data-type="link.type === 'Canvas' ? 'graph' : undefined">
|
||||
<div class="tree-item-inner">{{ link.title }}</div>
|
||||
</NuxtLink>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const hasChildren = computed(() => {
|
|||
<template>
|
||||
<div class="tree-item">
|
||||
<div class="tree-item-self" :class="{'is-clickable': hasChildren}" data-path="{{ props.link.title }}">
|
||||
<NuxtLink no-prefetch class="tree-item-inner" :href="{hash: '#' + props.link.id}">{{ props.link.text }}</NuxtLink>
|
||||
<NuxtLink no-prefetch class="tree-item-inner" :to="{ hash: '#' + props.link.id, force: true }">{{ props.link.text }}</NuxtLink>
|
||||
</div>
|
||||
<div class="tree-item-children">
|
||||
<TocLink v-if="hasChildren" v-for="link of props.link.children" :link="link" />
|
||||
|
|
|
|||
|
|
@ -1,37 +1,50 @@
|
|||
import type { Project } from "~/server/api/project.get";
|
||||
import type { Project } from "~/types/api";
|
||||
|
||||
export default function useProject()
|
||||
{
|
||||
const id = useState<number>("projectId", () => 1);
|
||||
const project = useCookie('project');
|
||||
|
||||
const id = useState<number>("projectId", () => parseInt(project.value ?? '0'));
|
||||
const name = useState<string>("projectName", undefined);
|
||||
const owner = useState<number>("projectOwner", undefined);
|
||||
const home = useState<string | null>("projectHomepage", () => null);
|
||||
const summary = useState<string | null>("projectSummary", () => null);
|
||||
|
||||
return {
|
||||
id, name, owner, home, get, set
|
||||
}
|
||||
id, name, owner, home, summary, get, set
|
||||
};
|
||||
}
|
||||
|
||||
async function get(): Promise<void> {
|
||||
async function get(): Promise<boolean> {
|
||||
const id = useState<number>("projectId");
|
||||
|
||||
if (!id.value)
|
||||
return;
|
||||
return false;
|
||||
|
||||
try {
|
||||
const result = await $fetch(`/api/project/${id}`) as Project;
|
||||
const result = await $fetch(`/api/project/${id.value}`) as Project;
|
||||
|
||||
const name = useState<string>("projectName");
|
||||
const owner = useState<number>("projectOwner");
|
||||
const home = useState<string | null>("projectHomepage");
|
||||
const summary = useState<string | null>("projectSummary");
|
||||
|
||||
name.value = result.name;
|
||||
owner.value = result.owner;
|
||||
home.value = result.home;
|
||||
} catch(e) {}
|
||||
summary.value = result.summary;
|
||||
|
||||
return true;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function set(id: number): void {
|
||||
async function set(id: number): Promise<boolean> {
|
||||
const _id = useState<number>("projectId");
|
||||
|
||||
_id.value = id;
|
||||
const project = useCookie('project');
|
||||
project.value = id.toString();
|
||||
|
||||
return await get();
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<div class="column">
|
||||
<textarea v-model="input"></textarea>
|
||||
<pre>{{ timing }}ms</pre>
|
||||
</div>
|
||||
<div class="column">
|
||||
<Suspense>
|
||||
<template #fallback>
|
||||
<div class="loading"></div>
|
||||
</template>
|
||||
<Markdown v-if="input.length > 0" :content="input" v-model="timing"/>
|
||||
</Suspense>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const input = ref(""), timing = ref(0);
|
||||
</script>
|
||||
|
|
@ -6,6 +6,10 @@ const { data: content } = await useFetch(`/api/project/${route.params.projectId}
|
|||
path: unifySlug(route.params.slug)
|
||||
}
|
||||
});
|
||||
|
||||
const { set } = useProject();
|
||||
|
||||
await set(parseInt(route.params.projectId as string));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ onMounted(() => {
|
|||
<ContentRenderer :value="getContent(descriptions)" />
|
||||
<h2>Pages contenant le tag</h2>
|
||||
<div class="card-container">
|
||||
<NuxtLink :key="item._id" :href="'/explorer' + item._path" v-for="item of list" class="tag"> {{ item.title }} </NuxtLink>
|
||||
<NuxtLink :key="item._id" :to="{ path: '/explorer' + item._path, force: true }" v-for="item of list" class="tag"> {{ item.title }} </NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
const { id: project } = useProject();
|
||||
|
||||
const { data: projects, status, error } = await useFetch('/api/project');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head>
|
||||
<Title>Liste des projets</Title>
|
||||
</Head>
|
||||
<div class="site-body-center-column">
|
||||
<div class="projects-container">
|
||||
<div v-if="status === 'success'" class="project-list">
|
||||
<div v-for="p of projects" class="project-item">
|
||||
<NuxtLink class="project-title" :to="{ path: `/explorer/${p.id}${p.home}`, force: true }">{{ p.name }}</NuxtLink>
|
||||
<div class="project-user">Par {{ p.username }}</div>
|
||||
<div class="project-pages">{{ p.pages }} pages</div>
|
||||
<div class="project-summary">{{ p.summary ?? "Sans contenu" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="status === 'pending'" class="loading"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -59,7 +59,7 @@ async function submit()
|
|||
placeholder="" title="Mot de passe"
|
||||
:error="passwordError" />
|
||||
<button>Se connecter</button>
|
||||
<NuxtLink to="/user/register">Pas de compte ?</NuxtLink>
|
||||
<NuxtLink :to="{ path: `/user/register`, force: true }">Pas de compte ?</NuxtLink>
|
||||
</form>
|
||||
<div v-else-if="status === AuthStatus.loading" class="input-form"><div class="loading"></div></div>
|
||||
<div v-else class="not-found-container">
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ async function submit()
|
|||
title="Confirmer le mot de passe"
|
||||
:error="confirmPassword === '' || confirmPassword === state.password ? '' : 'Les mots de passe saisies ne sont pas identique'" />
|
||||
<button>S'inscrire</button>
|
||||
<NuxtLink to="/user/login">Se connecter</NuxtLink>
|
||||
<NuxtLink :to="{ path: `/user/login`, force: true }">Se connecter</NuxtLink>
|
||||
</form>
|
||||
<div v-else-if="status === AuthStatus.loading" class="input-form"><div class="loading"></div></div>
|
||||
<div v-else class="not-found-container">
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -3,8 +3,8 @@ import useDatabase from '~/composables/useDatabase';
|
|||
export default defineEventHandler(async (e) => {
|
||||
const project = getRouterParam(e, "projectId");
|
||||
|
||||
const where = ["project = $project"];
|
||||
const criteria: Record<string, any> = { $project: project };
|
||||
const where = ["id = $id"];
|
||||
const criteria: Record<string, any> = { $id: project };
|
||||
|
||||
if (!!project) {
|
||||
const db = useDatabase();
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ export default defineEventHandler(async (e) => {
|
|||
if (query.search) {
|
||||
const db = useDatabase();
|
||||
|
||||
const projects = db.query(`SELECT * FROM explorer_projects WHERE name LIKE ?1`).all(query.search) as Project[];
|
||||
const files = db.query(`SELECT * FROM explorer_files WHERE title LIKE ?1 `).all(query.search) as File[];
|
||||
const users = db.query(`SELECT id, username FROM users WHERE username LIKE ?1 `).all(query.search) as User[];
|
||||
const projects = db.query(`SELECT p.*, u.username, COUNT(f.path) as pages FROM explorer_projects p LEFT JOIN users u ON p.owner = u.id LEFT JOIN explorer_files f ON f.project = p.id WHERE name LIKE ?1 AND f.type != "Folder" GROUP BY p.id`).all(query.search) as ProjectSearch[];
|
||||
const files = db.query(`SELECT f.*, u.username, count(c.path) as comments FROM explorer_files f LEFT JOIN users u ON f.owner = u.id LEFT JOIN explorer_comments c ON c.project = f.project AND c.path = f.path WHERE title LIKE ?1 AND private = 0 AND type != "Folder" GROUP BY f.project, f.path`).all(query.search) as FileSearch[];
|
||||
const users = db.query(`SELECT id, username FROM users WHERE username LIKE ?1`).all(query.search) as UserSearch[];
|
||||
|
||||
return {
|
||||
projects,
|
||||
|
|
|
|||
26
types/api.ts
26
types/api.ts
|
|
@ -3,6 +3,7 @@ export interface Project {
|
|||
name: string;
|
||||
owner: number;
|
||||
home: string;
|
||||
summary: string;
|
||||
}
|
||||
export interface Navigation {
|
||||
title: string;
|
||||
|
|
@ -32,8 +33,25 @@ export interface User {
|
|||
id: number;
|
||||
username: string;
|
||||
}
|
||||
export interface Search {
|
||||
projects: Project[];
|
||||
files: File[];
|
||||
users: User[];
|
||||
export interface ProjectSearch extends Project
|
||||
{
|
||||
pages: number;
|
||||
username: string;
|
||||
}
|
||||
export interface FileSearch extends File
|
||||
{
|
||||
comments: number;
|
||||
username: string;
|
||||
}
|
||||
export interface CommentSearch extends Comment
|
||||
{
|
||||
username: string;
|
||||
}
|
||||
export interface UserSearch extends User
|
||||
{
|
||||
}
|
||||
export interface Search {
|
||||
projects: ProjectSearch[];
|
||||
files: FileSearch[];
|
||||
users: UserSearch[];
|
||||
}
|
||||
Loading…
Reference in New Issue