feat(calendar): initial commit

This commit is contained in:
benimautner 2024-04-05 08:56:26 +02:00
parent f0d695e789
commit beac161ef8
8 changed files with 310 additions and 0 deletions

View File

@ -54,6 +54,10 @@
"@fortawesome/free-regular-svg-icons": "6.5.1",
"@fortawesome/free-solid-svg-icons": "6.5.1",
"@fortawesome/vue-fontawesome": "3.0.6",
"@fullcalendar/core": "^6.1.11",
"@fullcalendar/interaction": "^6.1.11",
"@fullcalendar/timegrid": "^6.1.11",
"@fullcalendar/vue3": "^6.1.11",
"@github/hotkey": "3.1.0",
"@infectoone/vue-ganttastic": "2.3.1",
"@intlify/unplugin-vue-i18n": "3.0.1",

View File

@ -25,6 +25,18 @@ dependencies:
'@fortawesome/vue-fontawesome':
specifier: 3.0.6
version: 3.0.6(@fortawesome/fontawesome-svg-core@6.5.1)(vue@3.4.21)
'@fullcalendar/core':
specifier: ^6.1.11
version: 6.1.11
'@fullcalendar/interaction':
specifier: ^6.1.11
version: 6.1.11(@fullcalendar/core@6.1.11)
'@fullcalendar/timegrid':
specifier: ^6.1.11
version: 6.1.11(@fullcalendar/core@6.1.11)
'@fullcalendar/vue3':
specifier: ^6.1.11
version: 6.1.11(@fullcalendar/core@6.1.11)(vue@3.4.21)
'@github/hotkey':
specifier: 3.1.0
version: 3.1.0(patch_hash=c67tdk7qpd5grxd2zj6lsxfbou)
@ -2693,6 +2705,47 @@ packages:
vue: 3.4.21(typescript@5.4.2)
dev: false
/@fullcalendar/core@6.1.11:
resolution: {integrity: sha512-TjG7c8sUz+Vkui2FyCNJ+xqyu0nq653Ibe99A66LoW95oBo6tVhhKIaG1Wh0GVKymYiqAQN/OEdYTuj4ay27kA==}
dependencies:
preact: 10.12.1
dev: false
/@fullcalendar/daygrid@6.1.11(@fullcalendar/core@6.1.11):
resolution: {integrity: sha512-hF5jJB7cgUIxWD5MVjj8IU407HISyLu7BWXcEIuTytkfr8oolOXeCazqnnjmRbnFOncoJQVstTtq6SIhaT32Xg==}
peerDependencies:
'@fullcalendar/core': ~6.1.11
dependencies:
'@fullcalendar/core': 6.1.11
dev: false
/@fullcalendar/interaction@6.1.11(@fullcalendar/core@6.1.11):
resolution: {integrity: sha512-ynOKjzuPwEAMgTQ6R/Z2zvzIIqG4p8/Qmnhi1q0vzPZZxSIYx3rlZuvpEK2WGBZZ1XEafDOP/LGfbWoNZe+qdg==}
peerDependencies:
'@fullcalendar/core': ~6.1.11
dependencies:
'@fullcalendar/core': 6.1.11
dev: false
/@fullcalendar/timegrid@6.1.11(@fullcalendar/core@6.1.11):
resolution: {integrity: sha512-0seUHK/ferH89IeuCvV4Bib0zWjgK0nsptNdmAc9wDBxD/d9hm5Mdti0URJX6bDoRtsSfRDu5XsRcrzwoc+AUQ==}
peerDependencies:
'@fullcalendar/core': ~6.1.11
dependencies:
'@fullcalendar/core': 6.1.11
'@fullcalendar/daygrid': 6.1.11(@fullcalendar/core@6.1.11)
dev: false
/@fullcalendar/vue3@6.1.11(@fullcalendar/core@6.1.11)(vue@3.4.21):
resolution: {integrity: sha512-jBoDS0WSpuOM9ZgjL3lNh6o385u/LthFZDaMUACjVVJZh3JuBbuA7ghdUvIelcTNXa5VRCkSZOpivTJWOnLfcg==}
peerDependencies:
'@fullcalendar/core': ~6.1.11
vue: ^3.0.11
dependencies:
'@fullcalendar/core': 6.1.11
vue: 3.4.21(typescript@5.4.2)
dev: false
/@github/hotkey@3.1.0(patch_hash=c67tdk7qpd5grxd2zj6lsxfbou):
resolution: {integrity: sha512-Lj9QjYa+b+Nk5U1nZtlXLdx3HI8/EeM6ZNwBjpYcGVYqpwHdM2ScRH0p7+5zh28JG6SPbTM9+Rb1dFd742qMTw==}
dev: false
@ -8483,6 +8536,10 @@ packages:
picocolors: 1.0.0
source-map-js: 1.2.0
/preact@10.12.1:
resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==}
dev: false
/prelude-ls@1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
engines: {node: '>= 0.8.0'}

View File

@ -0,0 +1,215 @@
<template>
<ProjectWrapper
ref="projectWrapper"
class="project-list"
:project-id="projectId"
:view-id
>
<template #header>
</template>
<template #default>
<FullCalendar :options="calendarOptions" ref="calendar"/>
</template>
</ProjectWrapper>
</template>
<script setup lang="ts">
import {ref, computed, nextTick, onMounted, watch, type Ref} from 'vue'
import draggable from 'zhyswan-vuedraggable'
import {isSavedFilter} from '@/services/savedFilter'
import ProjectWrapper from '@/components/project/ProjectWrapper.vue'
import FullCalendar from '@fullcalendar/vue3'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import type { IProject } from '@/modelTypes/IProject'
import type { IProjectView } from '@/modelTypes/IProjectView'
import { defineComponent } from 'vue'
import type { ITask } from '@/modelTypes/ITask'
import { useTaskList } from '@/composables/useTaskList'
import { elementClosest } from '@fullcalendar/core/internal'
const calendar = ref(null)
const projectWrapper = ref(null)
const {
projectId,
viewId,
} = defineProps<{
projectId: IProject['id'],
viewId: IProjectView['id'],
}>()
const {
tasks: allTasks,
loading,
totalPages,
currentPage,
loadTasks,
params,
sortByParam,
} = useTaskList(() => projectId, () => viewId)
const tasks = ref<ITask[]>([])
let eventGuid = 0
let todayStr = new Date().toISOString().replace(/T.*$/, '') // YYYY-MM-DD of today
/*
onMounted(() => {
if(calendar.value)
calendar.value.calendar.addEvent({
id: createEventId(),
title: 'All-day event',
start: todayStr
});
})
*/
let initialEvents = [];
watch(
allTasks,
() => {
console.log(allTasks.value);
for(let task of allTasks.value) {
if(task.startDate)
calendar.value.calendar.addEvent({
id: task.id,
title: task.title,
start: task.startDate,
end: task.endDate,
extendedProps: {
done: task.done
}
});
}
}
)
/*
const INITIAL_EVENTS = [
{
id: createEventId(),
title: 'All-day event',
start: todayStr
},
{
id: createEventId(),
title: 'Timed event',
start: todayStr + 'T12:00:00'
}
]*/
function createEventId() {
return String(eventGuid++)
}
/*
const {
projectId,
viewId,
} = defineProps<{
projectId: IProject['id'],
viewId: IProjectView['id'],
}>()
*/
function handleWeekendsToggle() {
calendarOptions.value.weekends = calendarOptions.value.weekends // update a property
};
function handleDateSelect(selectInfo: { view: { calendar: any }; startStr: any; endStr: any; allDay: any }) {
//let title = prompt('Please enter a new title for your event')
let title = "test"
let calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if (title) {
calendarApi.addEvent({
id: createEventId(),
title,
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay
})
}
};
function handleEventClick(clickInfo: { event: any }) {
if (confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.extendedProps.done != clickInfo.event.extendedProps.done
console.log("here")
clickInfo.view.calendar.render()
}
};
function parseTime(time: Date | null) {
if(time)
return time.getHours() + ":" + String(time.getMinutes()).padStart(2,0)
else
return ""
}
function eventRender (info: any) {
console.log(info);
const event = info.event;
console.log(event.extendedProps);
let timeEl = document.createElement("p");
let titleEl = document.createElement("p");
timeEl.innerHTML = parseTime(event.start)
titleEl.innerHTML = event.title
if(event.end)
timeEl.innerHTML += " - " + parseTime(event.end);
if(event.extendedProps.done) {
let strikethroughEl = document.createElement("s");
strikethroughEl.innerHTML = titleEl.innerHTML;
titleEl = strikethroughEl;
}
return {domNodes: [titleEl, timeEl]}
// {description: "Lecture", department: "BioChemistry"}
}
const calendarOptions = ref({
plugins: [
timeGridPlugin,
interactionPlugin // needed for dateClick
],
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'timeGridWeek,timeGridDay'
},
initialView: 'timeGridWeek',
initialEvents: initialEvents, // alternatively, use the `events` setting to fetch from a feed
editable: true,
selectable: true,
selectMirror: true,
dayMaxEvents: true,
weekends: true,
select: handleDateSelect,
eventClick: handleEventClick,
eventContent: eventRender
/* you can update a remote database when these fire:
eventAdd:
eventChange:
eventRemove:
*/
});
</script>

View File

@ -116,6 +116,9 @@ function validateTitle() {
<option value="kanban">
{{ $t('project.kanban.title') }}
</option>
<option value="calendar">
{{ $t('project.calendar.title') }}
</option>
</select>
</div>
</div>

View File

@ -362,6 +362,9 @@
"bucketLimitSavedSuccess": "The bucket limit been saved successfully.",
"collapse": "Collapse this bucket"
},
"calendar": {
"title": "Calendar"
},
"pseudo": {
"favorites": {
"title": "Favorites"

View File

@ -7,6 +7,7 @@ import ProjectList from '@/components/project/views/ProjectList.vue'
import ProjectGantt from '@/components/project/views/ProjectGantt.vue'
import ProjectTable from '@/components/project/views/ProjectTable.vue'
import ProjectKanban from '@/components/project/views/ProjectKanban.vue'
import ProjectCalendar from '@/components/project/views/ProjectCalendar.vue'
const {
projectId,
@ -77,4 +78,9 @@ const route = useRoute()
:project-id="projectId"
:view-id
/>
<ProjectCalendar
v-if="currentView?.viewKind === 'calendar'"
:project-id="projectId"
:view-id
/>
</template>

View File

@ -31,10 +31,12 @@ const viewToEdit = ref<IProjectView | null>(null)
async function createView() {
if (!showCreateForm.value) {
showCreateForm.value = true
alert(1)
return
}
if (newView.value.title === '') {
alert(2)
return
}
@ -50,6 +52,7 @@ async function createView() {
projectStore.setProjectView(result)
newView.value = new ProjectViewModel({})
} catch (e) {
alert(e)
error(e)
}
}

View File

@ -37,6 +37,8 @@ func (p *ProjectViewKind) MarshalJSON() ([]byte, error) {
return []byte(`"table"`), nil
case ProjectViewKindKanban:
return []byte(`"kanban"`), nil
case ProjectViewKindCalendar:
return []byte(`"calendar"`), nil
}
return []byte(`null`), nil
@ -58,6 +60,8 @@ func (p *ProjectViewKind) UnmarshalJSON(bytes []byte) error {
*p = ProjectViewKindTable
case "kanban":
*p = ProjectViewKindKanban
case "calendar":
*p = ProjectViewKindKanban
default:
return fmt.Errorf("unknown project view kind: %s", value)
}
@ -70,6 +74,7 @@ const (
ProjectViewKindGantt
ProjectViewKindTable
ProjectViewKindKanban
ProjectViewKindCalendar
)
type BucketConfigurationModeKind int
@ -420,6 +425,7 @@ func CreateDefaultViewsForProject(s *xorm.Session, project *Project, a web.Auth,
return
}
kanban := &ProjectView{
ProjectID: project.ID,
Title: "Kanban",
@ -432,11 +438,24 @@ func CreateDefaultViewsForProject(s *xorm.Session, project *Project, a web.Auth,
return
}
calendar := &ProjectView{
ProjectID: project.ID,
Title: "Calendar",
ViewKind: ProjectViewKindCalendar,
Position: 500,
}
err = createProjectView(s, calendar, a, createBacklogBucket)
if err != nil {
return
}
project.Views = []*ProjectView{
list,
gantt,
table,
kanban,
calendar,
}
return