Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection View #47

Merged
merged 34 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
29520d8
Add Sidebar Template.
FledgeXu Aug 16, 2023
f5a9d98
Add the Basic Sidebar.
FledgeXu Aug 16, 2023
a842249
Add Project Selection Function.
FledgeXu Aug 17, 2023
66aca58
Add changing project name function.
FledgeXu Aug 17, 2023
8b256a3
Add deleting project function.
FledgeXu Aug 17, 2023
cbc8819
Add Redirect.
FledgeXu Aug 17, 2023
14423a9
Remove border.
FledgeXu Aug 17, 2023
f0add15
Fix the double deletion bug.
FledgeXu Aug 17, 2023
95f5ec0
Please CI complain.
FledgeXu Aug 17, 2023
f5fd50e
Rname css class name.
FledgeXu Aug 17, 2023
f8bc64a
Fix typos and name issues.
FledgeXu Aug 18, 2023
fccc407
Refactor modal component.
FledgeXu Aug 18, 2023
3bd1001
Add deletion confirm to project sidebar.
FledgeXu Aug 18, 2023
fb5251d
Add Contact and FAQ pages.
FledgeXu Aug 18, 2023
10cb2b0
Fix text vertical glitch.
FledgeXu Aug 18, 2023
1e5e6c3
Fix existed user view.
FledgeXu Aug 18, 2023
a73f39c
Change cursor type
FledgeXu Aug 18, 2023
5d2b9a1
Adjust sidebar width.
FledgeXu Aug 18, 2023
7a164d0
Add Enter and Esc action.
FledgeXu Aug 18, 2023
f08b005
Dragging files arrive to sidebar collection view.
FledgeXu Aug 18, 2023
e6be842
Check project expire date when files are uploaded.
FledgeXu Aug 18, 2023
4bbcbf6
Please CI.
FledgeXu Aug 18, 2023
e7b191f
Fix typo.
FledgeXu Aug 21, 2023
c607e4d
Add a cursor to indicate project is editable.
FledgeXu Aug 21, 2023
dac93de
Fix Edit actions.
FledgeXu Aug 21, 2023
88e236d
Fix Modal's wrong name.
FledgeXu Aug 21, 2023
21640ea
Trimming too long text.
FledgeXu Aug 21, 2023
fd90b7d
Break the reference.
FledgeXu Aug 21, 2023
1d37fd0
Revert name when updating failed.
FledgeXu Aug 21, 2023
d677a48
Stop showing Delete Button when it's in edit mode.
FledgeXu Aug 21, 2023
070a481
Fix wrong argument.
FledgeXu Aug 21, 2023
918f2d8
Format code.
FledgeXu Aug 21, 2023
01cc6dc
Format code.
FledgeXu Aug 21, 2023
be6381e
Fix format conflict.
FledgeXu Aug 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
<template>
<div class="d-flex flex-column vh-100" v-if="isReady">
<div class="flex-shrink-1">
<Suspense>
<RouterView />
</Suspense>
</div>
<FooterComponent />
</div>
<Suspense v-if="isReady">
<RouterView />
</Suspense>
<AlertsComponentVue />
<ModalComponent />
</template>

<script setup lang="ts">
import AlertsComponentVue from './components/AlertsComponent.vue'
import FooterComponent from './components/FooterComponent.vue'
import ModalComponent from './components/ModalComponent.vue'
import { onMounted, ref } from 'vue'
import { useAppStore } from './stores/stores'

Expand Down
10 changes: 10 additions & 0 deletions frontend/src/components/ContactPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<template>
Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad
nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia.
Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla
est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat
reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident
adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit
commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea
consectetur et est culpa et culpa duis.
</template>
22 changes: 6 additions & 16 deletions frontend/src/components/DragToStartProjectComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@
import DragToStartField from '@/components/DragToStartField.vue'
import { type Project } from '@/constants'
import type { User } from '@/constants'
import { useAppStore, useProjectIdStore, useInitialFilesStore } from '@/stores/stores'
import { useAppStore, useProjectStore, useInitialFilesStore } from '@/stores/stores'
import { createNewProject } from '@/utils'

const storeProjectId = useProjectIdStore()
const storeProject = useProjectStore()
const storeApp = useAppStore()
const storeInitialFiles = useInitialFilesStore()

async function createUserAndProject(): Promise<[User | null, Project | null]> {
const projectRequestData = {
name: 'First Project'
}
var user: User | null = null
var project: Project | null = null

Expand All @@ -30,16 +28,7 @@ async function createUserAndProject(): Promise<[User | null, Project | null]> {
return [user, project]
}

try {
const createProjectResponse = await storeApp.axiosInstance.post<Project>(
'/projects',
projectRequestData
)
project = createProjectResponse.data
} catch (error: any) {
console.log('Unable to create a new project.', error)
storeApp.alertsError('Unable to create a new project.')
}
project = await createNewProject('First Project')

return [user, project]
}
Expand All @@ -54,6 +43,7 @@ function setProjectId(project: Project | null) {
if (project == null) {
return
}
storeProjectId.setProjectId(project.id)
storeProject.setProjects([project])
storeProject.setLastProjectId(project.id)
}
</script>
47 changes: 30 additions & 17 deletions frontend/src/components/ModalComponent.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
<template>
<div class="modal fade show" tabindex="-1" ref="modal">
<div class="modal fade" tabindex="-1" ref="modal">
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">{{ props.title }}</h1>
<h1 class="modal-title fs-5">{{ storeModal.title }}</h1>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
@click.prevent="storeModal.dismissModal"
></button>
</div>
<div class="modal-body">
<slot />
<ul>
<li v-for="(element, key) in storeModal.content" :key="key">
{{ element }}
</li>
</ul>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
@click.prevent="emit('clickSecondaryButton')"
@click.prevent="clickButton(storeModal.clickSecondaryButton)"
>
{{ props.secondaryButtonTitle }}
{{ storeModal.secondaryButtonTitle }}
</button>
<button
type="button"
class="btn btn-primary"
data-bs-dismiss="modal"
@click.prevent="emit('clickPrimaryButton')"
@click.prevent="clickButton(storeModal.clickPrimaryButton)"
>
{{ props.primaryButtonTitle }}
{{ storeModal.primaryButtonTitle }}
</button>
</div>
</div>
Expand All @@ -38,25 +43,33 @@
</template>

<script setup lang="ts">
import { useModalStore } from '@/stores/stores'
import * as bootstrap from 'bootstrap'
import { ref, type Ref } from 'vue'
import { ref, watch, type Ref } from 'vue'

const storeModal = useModalStore()

const props = defineProps<{
title: string
primaryButtonTitle: string
secondaryButtonTitle: string
}>()
const emit = defineEmits<{
clickPrimaryButton: []
clickSecondaryButton: []
}>()
const modal: Ref<Element | null> = ref(null)
defineExpose({ showModal })

watch(
() => storeModal.isShown,
(newValue) => {
if (newValue) {
showModal()
}
}
)

function showModal() {
if (modal.value != null) {
const confirmModal = new bootstrap.Modal(modal.value)
confirmModal.show()
}
}

function clickButton(action: () => Promise<void>) {
storeModal.dismissModal()
action()
}
</script>
144 changes: 144 additions & 0 deletions frontend/src/components/ProjectColumnComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<template>
<div
class="px-2 py-2 my-2 d-flex justify-content-between align-items-center project-column"
:class="{ active: isActive }"
@click.prevent="setupProject"
@dblclick.prevent="enableEditMode"
@mouseover="isHover = true"
@mouseleave="isHover = false"
>
<div class="d-flex align-items-center">
<div v-if="!isEditMode" class="text-light fs-4 pe-1 me-1">
<font-awesome-icon :icon="['fa', 'file']" />
</div>
<input
ref="inputElement"
v-else
type="text"
class="form-control"
@blur="exitEditModeWithoutChange"
@keyup.esc="exitEditModeWithoutChange"
@keyup.enter="exitEditModeWithChange"
v-model="editingProjectName"
/>
<div v-if="!isEditMode" class="fw-semibold text-light text-truncate project-name">
{{ projectName }}
</div>
</div>
<div v-show="!isHover" class="expire text-white-50">
{{ leftDays }}
</div>
<button
v-show="isHover && !isEditMode"
type="button"
class="btn text-light py-9"
@click.stop="clickDeleteProjectButton"
>
<font-awesome-icon :icon="['fas', 'trash']" />
</button>
</div>
</template>

<script setup lang="ts">
import type { Project } from '@/constants'
import { useAppStore, useModalStore, useProjectStore } from '@/stores/stores'
import moment from 'moment'
import { computed, ref, watch, type Ref } from 'vue'

const props = defineProps<{ project: Project }>()
const emit = defineEmits<{ deleteProject: [Project] }>()

const leftDays = computed(() =>
props.project.expire_on ? `Expire ${moment.utc(props.project.expire_on).fromNow()}` : ''
FledgeXu marked this conversation as resolved.
Show resolved Hide resolved
)
const storeProject = useProjectStore()
const storeModal = useModalStore()
const isActive = computed(() => storeProject.lastProjectId == props.project.id)
const isEditMode = ref(false)
const isHover = ref(false)
const projectName = ref(props.project.name)
const editingProjectName = ref(projectName.value)
const inputElement: Ref<HTMLInputElement | null> = ref(null)
const storeApp = useAppStore()

watch(inputElement, (newElement) => {
newElement?.focus()
})

watch(projectName, (newValue) => {
editingProjectName.value = newValue
})

function setupProject() {
storeProject.setLastProjectId(props.project.id)
}

function enableEditMode() {
isEditMode.value = true
}

async function exitEditModeWithChange() {
isEditMode.value = false
await updateProjectName(props.project.id, editingProjectName.value)
}

async function exitEditModeWithoutChange() {
isEditMode.value = false
editingProjectName.value = projectName.value
}

async function updateProjectName(projectId: string, newName: string) {
const projectRequestData = {
name: newName
}
try {
await storeApp.axiosInstance.patch<Project>(`/projects/${projectId}`, projectRequestData)
} catch (error: any) {
console.log('Unable to update project name.', error, projectId)
storeApp.alertsError(`Unable to update project name, project id: ${projectId}`)
FledgeXu marked this conversation as resolved.
Show resolved Hide resolved
editingProjectName.value = projectName.value
}
projectName.value = newName
}

async function deleteProject() {
try {
await storeApp.axiosInstance.delete(`/projects/${props.project.id}`)
} catch (error: any) {
console.log('Unable to delete project.', error, props.project.id)
storeApp.alertsError(`Unable to delete project, project id: ${props.project.id}`)
}
emit('deleteProject', props.project)
}

async function clickDeleteProjectButton() {
storeModal.showModal(
'Are you sure you want to delete:',
'Delete',
'Close',
deleteProject,
async () => {},
[projectName.value]
)
}
</script>

<style scoped>
.expire {
font-size: 0.8em;
}

.active {
background-color: orange;
}

.project-column {
cursor: pointer;
height: 3em;
}

.project-name {
cursor: text;
max-width: 8em;
}
</style>
50 changes: 50 additions & 0 deletions frontend/src/components/SideBarComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<template>
<div class="d-flex flex-column" style="height: 100%">
<div class="d-flex justify-content-between mt-2 border-bottom border-2 border-white">
<div class="fw-bold fs-4 text-light">Collection:</div>
<button
type="button"
class="btn fw-bold fs-5 text-light p-1"
@click.prevent="createAndUpdateProject"
>
<font-awesome-icon :icon="['fas', 'plus']" />
</button>
</div>
<div class="flex-grow-1 py-2 overflow-x-auto">
<ProjectColumnComponent
v-for="project in storeProject.projects"
:key="project.id"
:project="project"
@delete-project="removeProjectFromList"
/>
</div>
<div class="border-top border-2 border-white py-2">
<p class="text-light"><router-link to="/faq" class="link-light">FAQ</router-link></p>
<p class="text-light"><router-link to="/contact" class="link-light">Contact</router-link></p>
</div>
</div>
</template>

<script setup lang="ts">
import ProjectColumnComponent from './ProjectColumnComponent.vue'
import type { Project } from '@/constants'
import { useProjectStore } from '@/stores/stores'
import { createNewProject, updateProjects } from '@/utils'
const storeProject = useProjectStore()

await updateProjects()

async function createAndUpdateProject() {
await createNewProject('New Project')
await updateProjects()
}

function removeProjectFromList(project: Project) {
storeProject.setProjects(storeProject.projects.filter((element) => element.id != project.id))
if (storeProject.projects.length != 0) {
storeProject.setLastProjectId(storeProject.projects[0].id)
} else {
storeProject.clearLastProjectId()
}
}
</script>
5 changes: 3 additions & 2 deletions frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import {
faFilePen,
faAngleDown,
faCheck,
faSort
faSort,
faFile
} from '@fortawesome/free-solid-svg-icons'

/* add icons to the library */
library.add(faPlus, faMinus, faXmark, faTrash, faFilePen, faAngleDown, faCheck, faSort)
library.add(faPlus, faMinus, faXmark, faTrash, faFilePen, faAngleDown, faCheck, faSort, faFile)

import App from './App.vue'
import router from './router'
Expand Down
Loading