213 lines
4.7 KiB
Vue
213 lines
4.7 KiB
Vue
<template>
|
|
<div
|
|
:class="{
|
|
'has-background': background,
|
|
'link-share-is-fullwidth': isFullWidth,
|
|
}"
|
|
:style="{'background-image': `url(${background})`}"
|
|
class="link-share-container"
|
|
>
|
|
<div class="has-text-centered link-share-view">
|
|
<Logo
|
|
v-if="logoVisible"
|
|
class="logo"
|
|
/>
|
|
<Message
|
|
v-if="projectLoadError"
|
|
variant="danger"
|
|
class="mbe-4"
|
|
>
|
|
{{ $t('sharing.projectLoadError') }}
|
|
<BaseButton
|
|
variant="secondary"
|
|
class="mls-2"
|
|
@click="retryProjectLoad"
|
|
>
|
|
{{ $t('sharing.retry') }}
|
|
</BaseButton>
|
|
</Message>
|
|
<BaseButton
|
|
v-if="!projectLoadError && currentProject"
|
|
:to="getProjectRoute()"
|
|
variant="text"
|
|
class="project-title-button"
|
|
:class="{'m-0': !logoVisible}"
|
|
>
|
|
<h1 class="title clickable-title">
|
|
{{ currentProject?.title === '' ? $t('misc.loading') : currentProject?.title }}
|
|
</h1>
|
|
</BaseButton>
|
|
<h1
|
|
v-else-if="!projectLoadError"
|
|
:class="{'m-0': !logoVisible}"
|
|
class="title"
|
|
>
|
|
{{ $t('misc.loading') }}
|
|
</h1>
|
|
<div class="box has-text-start view">
|
|
<RouterView />
|
|
<PoweredByLink utm-medium="link_share" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import {computed, ref, watch, onMounted} from 'vue'
|
|
import {useRoute} from 'vue-router'
|
|
|
|
import {useBaseStore} from '@/stores/base'
|
|
import {useProjectStore} from '@/stores/projects'
|
|
import {useLabelStore} from '@/stores/labels'
|
|
import {useAuthStore} from '@/stores/auth'
|
|
|
|
import Logo from '@/components/home/Logo.vue'
|
|
import PoweredByLink from './PoweredByLink.vue'
|
|
import BaseButton from '@/components/base/BaseButton.vue'
|
|
import Message from '@/components/misc/Message.vue'
|
|
import {PROJECT_VIEW_KINDS} from '@/modelTypes/IProjectView'
|
|
|
|
const baseStore = useBaseStore()
|
|
const projectStore = useProjectStore()
|
|
const authStore = useAuthStore()
|
|
const route = useRoute()
|
|
|
|
const currentProject = computed(() => baseStore.currentProject)
|
|
const background = computed(() => baseStore.background)
|
|
const logoVisible = computed(() => baseStore.logoVisible)
|
|
const projectLoadError = ref(false)
|
|
|
|
projectStore.loadAllProjects()
|
|
|
|
const labelStore = useLabelStore()
|
|
labelStore.loadAllLabels()
|
|
|
|
// Ensure project is loaded for link share
|
|
async function ensureProjectLoaded() {
|
|
if (!authStore.authLinkShare || !route.params.projectId) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
projectLoadError.value = false
|
|
|
|
// Load project if not already loaded
|
|
const projectId = Number(route.params.projectId)
|
|
if (!currentProject.value || currentProject.value.id !== projectId) {
|
|
await projectStore.loadProject(projectId)
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to load project for link share:', e)
|
|
projectLoadError.value = true
|
|
}
|
|
}
|
|
|
|
async function retryProjectLoad() {
|
|
await ensureProjectLoaded()
|
|
}
|
|
|
|
// Watch for route changes and ensure project is loaded
|
|
watch(() => route.params.projectId, ensureProjectLoaded, { immediate: true })
|
|
|
|
onMounted(ensureProjectLoaded)
|
|
|
|
function getProjectRoute() {
|
|
if (!currentProject.value) return undefined
|
|
|
|
const hash = route.hash // Preserve link share hash
|
|
|
|
// Default to the first available view or list view
|
|
const projectId = currentProject.value.id
|
|
const firstView = projectStore.projects[projectId]?.views?.[0]
|
|
|
|
if (firstView) {
|
|
return {
|
|
name: 'project.view',
|
|
params: { projectId, viewId: firstView.id },
|
|
hash,
|
|
}
|
|
}
|
|
|
|
return {
|
|
name: 'project.index',
|
|
params: { projectId },
|
|
hash,
|
|
}
|
|
}
|
|
|
|
const isFullWidth = computed(() => {
|
|
const viewId = route.params?.viewId ?? null
|
|
const projectId = route.params?.projectId ?? null
|
|
if (!viewId || !projectId) {
|
|
return false
|
|
}
|
|
|
|
const view = projectStore.projects[Number(projectId)]?.views.find(v => v.id === Number(viewId))
|
|
|
|
return view?.viewKind === PROJECT_VIEW_KINDS.KANBAN ||
|
|
view?.viewKind === PROJECT_VIEW_KINDS.GANTT
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.link-share-container.has-background .view {
|
|
background-color: transparent;
|
|
border: none;
|
|
}
|
|
|
|
.logo {
|
|
max-inline-size: 300px;
|
|
inline-size: 90%;
|
|
margin: 1rem auto 2rem;
|
|
block-size: 100px;
|
|
}
|
|
|
|
.title {
|
|
text-shadow: 0 0 1rem var(--white);
|
|
}
|
|
|
|
.project-title-button {
|
|
background: none !important;
|
|
border: none !important;
|
|
padding: 0 !important;
|
|
text-decoration: none !important;
|
|
|
|
&:hover .clickable-title {
|
|
opacity: 0.8;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
.clickable-title {
|
|
text-shadow: 0 0 1rem var(--white);
|
|
margin: 0;
|
|
|
|
&:hover {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
.link-share-view {
|
|
inline-size: 100%;
|
|
max-inline-size: $desktop;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.link-share-container.link-share-is-fullwidth {
|
|
.link-share-view {
|
|
max-inline-size: 100vw;
|
|
}
|
|
}
|
|
|
|
.link-share-container:not(.has-background) {
|
|
:deep(.loader-container, .gantt-chart-container > .card) {
|
|
box-shadow: none !important;
|
|
border: none;
|
|
|
|
.task-add {
|
|
padding: 1rem 0 0;
|
|
}
|
|
}
|
|
}
|
|
</style>
|