Compare commits

...
This repository has been archived on 2024-02-08. You can view files and clone it, but cannot push or open issues or pull requests.

6 Commits

Author SHA1 Message Date
jackymancs4 16eef6ddf3 Merge branch 'main' into feature/precision-gantt 2023-10-05 09:15:06 +00:00
renovate bc9bfe3300 fix(deps): update dependency marked to v9.1.0 (#3760)
Reviewed-on: #3760
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-05 07:06:20 +00:00
Giacomo Rossetto ffa6ce1714 Remove date-to-string handling
Restored snappy behavior on day precision mode
2023-10-04 18:30:12 +00:00
jackymancs4 c59e34f4bf Merge branch 'main' into feature/precision-gantt 2023-10-04 14:56:13 +00:00
renovate c2005c6c71 chore(deps): update dev-dependencies (#3757)
Reviewed-on: #3757
Reviewed-by: konrad <k@knt.li>
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-04 06:48:23 +00:00
Giacomo Rossetto 9b912af873 Initial work 2023-10-03 22:18:25 +00:00
8 changed files with 529 additions and 480 deletions

View File

@ -74,7 +74,7 @@
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lodash.debounce": "4.0.8",
"marked": "9.0.3",
"marked": "9.1.0",
"pinia": "2.1.6",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
@ -95,51 +95,51 @@
"@faker-js/faker": "8.1.0",
"@histoire/plugin-screenshot": "0.17.0",
"@histoire/plugin-vue": "0.17.1",
"@rushstack/eslint-patch": "1.5.0",
"@rushstack/eslint-patch": "1.5.1",
"@tsconfig/node18": "18.2.2",
"@types/codemirror": "5.60.10",
"@types/dompurify": "3.0.3",
"@types/flexsearch": "0.7.4",
"@types/is-touch-device": "1.0.0",
"@types/lodash.debounce": "4.0.7",
"@types/marked": "5.0.1",
"@types/node": "18.18.0",
"@types/marked": "5.0.2",
"@types/node": "18.18.3",
"@types/postcss-preset-env": "7.7.0",
"@types/sortablejs": "1.15.3",
"@typescript-eslint/eslint-plugin": "6.7.3",
"@typescript-eslint/parser": "6.7.3",
"@typescript-eslint/eslint-plugin": "6.7.4",
"@typescript-eslint/parser": "6.7.4",
"@vitejs/plugin-legacy": "4.1.1",
"@vitejs/plugin-vue": "4.3.4",
"@vitejs/plugin-vue": "4.4.0",
"@vue/eslint-config-typescript": "11.0.3",
"@vue/test-utils": "2.4.1",
"@vue/tsconfig": "0.4.0",
"autoprefixer": "10.4.16",
"browserslist": "4.22.0",
"caniuse-lite": "1.0.30001540",
"browserslist": "4.22.1",
"caniuse-lite": "1.0.30001543",
"css-has-pseudo": "6.0.0",
"csstype": "3.1.2",
"cypress": "12.17.4",
"esbuild": "0.19.3",
"esbuild": "0.19.4",
"eslint": "8.50.0",
"eslint-plugin-vue": "9.17.0",
"happy-dom": "10.11.2",
"histoire": "0.17.2",
"postcss": "8.4.30",
"postcss": "8.4.31",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "4.0.0",
"postcss-focus-within": "8.0.0",
"postcss-preset-env": "9.1.4",
"rollup": "3.29.3",
"rollup": "3.29.4",
"rollup-plugin-visualizer": "5.9.2",
"sass": "1.68.0",
"start-server-and-test": "2.0.1",
"typescript": "5.2.2",
"vite": "4.4.9",
"vite": "4.4.10",
"vite-plugin-inject-preload": "1.3.3",
"vite-plugin-pwa": "0.16.5",
"vite-plugin-sentry": "1.3.0",
"vite-svg-loader": "4.0.0",
"vitest": "0.34.5",
"vitest": "0.34.6",
"vue-tsc": "1.8.15",
"wait-on": "7.0.1",
"workbox-cli": "7.0.0"

File diff suppressed because it is too large Load Diff

View File

@ -5,10 +5,9 @@
/>
<div ref="ganttContainer" class="gantt-container" v-else>
<GGanttChart
:date-format="DAYJS_ISO_DATE_FORMAT"
:chart-start="isoToKebabDate(filters.dateFrom)"
:chart-end="isoToKebabDate(filters.dateTo)"
precision="day"
:chart-start="dateFromDate"
:chart-end="dateToDate"
:precision="ganttChartPrecision"
bar-start="startDate"
bar-end="endDate"
:grid="true"
@ -40,15 +39,9 @@
<script setup lang="ts">
import {computed, ref, watch, toRefs, onActivated} from 'vue'
import {useRouter} from 'vue-router'
import {getHexColor} from '@/models/task'
import {colorIsDark} from '@/helpers/color/colorIsDark'
import {isoToKebabDate} from '@/helpers/time/isoToKebabDate'
import {parseKebabDate} from '@/helpers/time/parseKebabDate'
import type {ITask, ITaskPartialWithId} from '@/modelTypes/ITask'
import type {DateISO} from '@/types/DateISO'
import type {GanttFilters} from '@/views/project/helpers/useGanttFilters'
import {
@ -66,12 +59,10 @@ export interface GanttChartProps {
isLoading: boolean,
filters: GanttFilters,
tasks: Map<ITask['id'], ITask>,
defaultTaskStartDate: DateISO
defaultTaskEndDate: DateISO
defaultTaskStartDate: Date
defaultTaskEndDate: Date
}
const DAYJS_ISO_DATE_FORMAT = 'YYYY-MM-DD'
const props = defineProps<GanttChartProps>()
const emit = defineEmits<{
@ -93,16 +84,28 @@ const dateFromDate = computed(() => new Date(new Date(filters.value.dateFrom).se
const dateToDate = computed(() => new Date(new Date(filters.value.dateTo).setHours(23,59,0,0)))
const DAY_WIDTH_PIXELS = 30
const DAY_HOUR_PRECISION_THRESHOLD = 5
const ganttChartWidth = computed(() => {
const ganttContainerReference = ganttContainer?.value
const ganttContainerWidth = ganttContainerReference ? (ganttContainerReference['clientWidth'] ?? 0) : 0
const dateDiff = Math.floor((dateToDate.value.valueOf() - dateFromDate.value.valueOf()) / MILLISECONDS_A_DAY)
const calculatedWidth = dateDiff * DAY_WIDTH_PIXELS
let calculatedWidth = dateDiff * DAY_WIDTH_PIXELS
if(dateDiff < DAY_HOUR_PRECISION_THRESHOLD) {
calculatedWidth *= 24
}
return (calculatedWidth > ganttContainerWidth) ? calculatedWidth + 'px' : '100%'
})
const ganttChartPrecision = computed(() => {
const dateDiff = Math.floor((dateToDate.value.valueOf() - dateFromDate.value.valueOf()) / MILLISECONDS_A_DAY)
return dateDiff < DAY_HOUR_PRECISION_THRESHOLD ? 'hour' : 'day'
})
const ganttBars = ref<GanttBarObject[][]>([])
@ -121,9 +124,10 @@ watch(
function transformTaskToGanttBar(t: ITask) {
const black = 'var(--grey-800)'
return [{
startDate: isoToKebabDate(t.startDate ? t.startDate.toISOString() : props.defaultTaskStartDate),
endDate: isoToKebabDate(t.endDate ? t.endDate.toISOString() : props.defaultTaskEndDate),
startDate: t.startDate ?? props.defaultTaskStartDate,
endDate: t.endDate ?? props.defaultTaskEndDate,
ganttBarConfig: {
id: String(t.id),
label: t.title,
@ -143,10 +147,32 @@ async function updateGanttTask(e: {
e: MouseEvent;
datetime?: string | undefined;
}) {
const taskId = Number(e.bar.ganttBarConfig.id)
const task = tasks.value.get(taskId)
const startDate: Date = new Date(e.bar.startDate)
const endDate: Date = new Date(e.bar.endDate)
if(task && ganttChartPrecision.value == 'day') {
if(task.startDate) {
startDate.setHours(task.startDate.getHours(), task.startDate.getMinutes(), task.startDate.getSeconds())
} else {
startDate.setHours(0,0,0)
}
if(task.endDate) {
endDate.setHours(task.endDate.getHours(), task.endDate.getMinutes(), task.endDate.getSeconds())
} else {
endDate.setHours(23,59,59)
}
}
emit('update:task', {
id: Number(e.bar.ganttBarConfig.id),
startDate: new Date(parseKebabDate(e.bar.startDate).setHours(0,0,0,0)),
endDate: new Date(parseKebabDate(e.bar.endDate).setHours(23,59,0,0)),
id: Number(taskId),
startDate: startDate,
endDate: endDate,
})
}

View File

@ -1,4 +1,5 @@
export const DATEFNS_DATE_FORMAT_KEBAB = 'yyyy-LL-dd'
export const DATEFNS_DATETIME_FORMAT_KEBAB = 'yyyy-LL-dd HH:mm'
export const SECONDS_A_MINUTE = 60
export const SECONDS_A_HOUR = SECONDS_A_MINUTE * 60

View File

@ -1,5 +1,5 @@
import type {DateISO} from '@/types/DateISO'
import type {DateKebab} from '@/types/DateKebab'
import type {DateKebab, TimeKebab} from '@/types/DateKebab'
// ✅ Format a date to YYYY-MM-DD (or any other format)
function padTo2Digits(num: number) {
@ -13,4 +13,16 @@ export function isoToKebabDate(isoDate: DateISO) {
padTo2Digits(date.getMonth() + 1), // January is 0, but we want it to be 1
padTo2Digits(date.getDate()),
].join('-') as DateKebab
}
export function isoToKebabTime(isoDate: DateISO) {
const date = new Date(isoDate)
return [
date.getFullYear(),
padTo2Digits(date.getMonth() + 1), // January is 0, but we want it to be 1
padTo2Digits(date.getDate()),
].join('-') + ' ' + [
padTo2Digits(date.getHours()), // January is 0, but we want it to be 1
padTo2Digits(date.getMinutes()),
].join(':') as TimeKebab
}

View File

@ -1,7 +1,11 @@
import {parse} from 'date-fns'
import {DATEFNS_DATE_FORMAT_KEBAB} from '@/constants/date'
import type {DateKebab} from '@/types/DateKebab'
import {DATEFNS_DATE_FORMAT_KEBAB, DATEFNS_DATETIME_FORMAT_KEBAB} from '@/constants/date'
import type {DateKebab, TimeKebab} from '@/types/DateKebab'
export function parseKebabDate(date: DateKebab): Date {
return parse(date, DATEFNS_DATE_FORMAT_KEBAB, new Date())
}
export function parseKebabDateTime(date: TimeKebab): Date {
return parse(date, DATEFNS_DATETIME_FORMAT_KEBAB, new Date())
}

View File

@ -2,3 +2,8 @@
* Date in Format 2022-12-10
*/
export type DateKebab = `${string}-${string}-${string}`
/**
* Date in Format 2022-12-10 00:00
*/
export type TimeKebab = `${string}-${string}-${string} ${string}:${string}`

View File

@ -65,7 +65,6 @@ import {createAsyncComponent} from '@/helpers/createAsyncComponent'
import {useGanttFilters} from './helpers/useGanttFilters'
import {RIGHTS} from '@/constants/rights'
import type {DateISO} from '@/types/DateISO'
import type {ITask} from '@/modelTypes/ITask'
type Options = Flatpickr.Options.Options
@ -91,12 +90,12 @@ const {
const DEFAULT_DATE_RANGE_DAYS = 7
const today = new Date()
const defaultTaskStartDate: DateISO = new Date(today.setHours(0, 0, 0, 0)).toISOString()
const defaultTaskEndDate: DateISO = new Date(new Date(
const defaultTaskStartDate: Date = new Date(today.setHours(0, 0, 0, 0))
const defaultTaskEndDate: Date = new Date(new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + DEFAULT_DATE_RANGE_DAYS,
).setHours(23, 59, 0, 0)).toISOString()
).setHours(23, 59, 0, 0))
async function addGanttTask(title: ITask['title']) {
return await addTask({