poprawki widget vikunja:

- poprawka kalendarza do wybierania daty (dalej nie dziala wszytko tak
  jakbym chcial)
pull/878/head
Bartosz Nikitiuk 2025-11-21 22:19:45 +07:00
parent 761265a4dc
commit 3885941397
No known key found for this signature in database
GPG Key ID: 725EADAB8CDD0DB1
3 changed files with 257 additions and 64 deletions

@ -337,31 +337,125 @@
color: white;
}
@media (max-width: 768px) {
.vikunja-table th, .vikunja-table td {
padding: 0.5rem 0.2rem;
font-size: 0.9rem;
}
.vikunja-table .label {
font-size: 0.8rem;
padding: 0.1rem 0.3rem;
}
.vikunja-modal-content {
width: 98%;
margin: 1rem;
}
.vikunja-modal-body {
padding: 1rem;
}
.vikunja-modal-header {
padding: 1rem;
}
.vikunja-modal-footer {
padding: 1rem;
}
/* Flatpickr Customization for Glance Theme */
.flatpickr-calendar {
background: var(--color-widget-background) !important;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3) !important;
border: 1px solid var(--color-separator) !important;
color: var(--color-text-base) !important;
font-family: 'JetBrains Mono', monospace !important;
}
.flatpickr-calendar.arrowTop:before, .flatpickr-calendar.arrowTop:after {
border-bottom-color: var(--color-widget-background) !important;
}
.flatpickr-calendar.arrowBottom:before, .flatpickr-calendar.arrowBottom:after {
border-top-color: var(--color-widget-background) !important;
}
.flatpickr-month {
background: var(--color-widget-background) !important;
color: var(--color-text-base) !important;
fill: var(--color-text-base) !important;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
background: var(--color-widget-background) !important;
color: var(--color-text-base) !important;
}
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
background: var(--color-widget-background-highlight) !important;
}
.flatpickr-current-month input.cur-year {
color: var(--color-text-base) !important;
}
.flatpickr-weekdays {
background: var(--color-widget-background) !important;
}
span.flatpickr-weekday {
color: var(--color-text-subdue) !important;
}
.flatpickr-day {
color: var(--color-text-base) !important;
}
.flatpickr-day.flatpickr-disabled, .flatpickr-day.flatpickr-disabled:hover {
color: var(--color-text-subdue) !important;
}
.flatpickr-day:hover, .flatpickr-day.prevMonthDay:hover, .flatpickr-day.nextMonthDay:hover, .flatpickr-day:focus, .flatpickr-day.prevMonthDay:focus, .flatpickr-day.nextMonthDay:focus {
background: var(--color-widget-background-highlight) !important;
border-color: var(--color-widget-background-highlight) !important;
}
.flatpickr-day.today {
border-color: var(--color-primary) !important;
}
.flatpickr-day.today:hover, .flatpickr-day.today:focus {
background: var(--color-primary) !important;
color: white !important;
}
.flatpickr-day.selected, .flatpickr-day.startRange, .flatpickr-day.endRange, .flatpickr-day.selected.inRange, .flatpickr-day.startRange.inRange, .flatpickr-day.endRange.inRange, .flatpickr-day.selected:focus, .flatpickr-day.startRange:focus, .flatpickr-day.endRange:focus, .flatpickr-day.selected:hover, .flatpickr-day.startRange:hover, .flatpickr-day.endRange:hover, .flatpickr-day.selected.prevMonthDay, .flatpickr-day.startRange.prevMonthDay, .flatpickr-day.endRange.prevMonthDay, .flatpickr-day.selected.nextMonthDay, .flatpickr-day.startRange.nextMonthDay, .flatpickr-day.endRange.nextMonthDay {
background: var(--color-primary) !important;
border-color: var(--color-primary) !important;
color: white !important;
}
.flatpickr-time {
border-top: 1px solid var(--color-separator) !important;
}
.flatpickr-time input {
color: var(--color-text-base) !important;
}
.flatpickr-time input:hover, .flatpickr-time .flatpickr-am-pm:hover, .flatpickr-time input:focus, .flatpickr-time .flatpickr-am-pm:focus {
background: var(--color-widget-background-highlight) !important;
}
.flatpickr-time .flatpickr-time-separator, .flatpickr-time .flatpickr-am-pm {
color: var(--color-text-base) !important;
}
/* Input Group Styling */
.vikunja-input-group {
position: relative;
width: 100%;
}
.vikunja-input-group .vikunja-input {
width: 100%;
padding-right: 2.5rem;
border-radius: 0.3rem;
border: 1px solid var(--color-separator);
}
.vikunja-input-icon {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 2.5rem;
background: transparent;
border: none;
color: var(--color-text-base);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
transition: color 0.2s;
}
.vikunja-input-icon:hover {
background: transparent;
color: var(--color-primary);
}

@ -1,7 +1,52 @@
// Vikunja widget interactivity
let flatpickrLoadingPromise = null;
function loadFlatpickr() {
if (window.flatpickr) return Promise.resolve();
if (flatpickrLoadingPromise) return flatpickrLoadingPromise;
flatpickrLoadingPromise = new Promise(async (resolve, reject) => {
try {
// Load CSS
const cssLink = document.createElement("link");
cssLink.rel = "stylesheet";
cssLink.href = "https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css";
document.head.appendChild(cssLink);
// Load JS
await new Promise((res, rej) => {
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/flatpickr";
script.onload = res;
script.onerror = rej;
document.head.appendChild(script);
});
// Load Locale
await new Promise((res, rej) => {
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/pl.js";
script.onload = res;
script.onerror = rej;
document.head.appendChild(script);
});
resolve();
} catch (e) {
console.error("Failed to load flatpickr", e);
reject(e);
}
});
return flatpickrLoadingPromise;
}
function initVikunjaWidget(widget) {
if (!widget) return;
// Start loading flatpickr immediately
loadFlatpickr();
{
const widgetID = widget.querySelector('.vikunja-table')?.dataset.widgetId ||
widget.querySelector('.vikunja-add-btn')?.dataset.widgetId;
@ -107,7 +152,43 @@ function initVikunjaWidget(widget) {
}
}
function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLabelIDs, row) {
function initFlatpickr(element, defaultDate) {
if (element._flatpickr) {
element._flatpickr.destroy();
}
if (!window.flatpickr) {
console.error("Flatpickr not loaded yet!");
return;
}
// Find the wrapper element (parent of the input)
const wrapper = element.closest('.flatpickr-wrapper');
const fp = flatpickr(wrapper || element, {
wrap: true, // Enable wrap mode to use external toggle
enableTime: true,
dateFormat: "Y-m-d H:i",
time_24hr: true,
locale: "pl",
defaultDate: defaultDate || null,
disableMobile: true,
allowInput: true, // Allow manual input
clickOpens: false // Only open on toggle button click
});
// Ensure the input element has reference to the flatpickr instance
// This is needed because we initialize on the wrapper but access it via the input in other functions
if (wrapper && element !== wrapper) {
element._flatpickr = fp;
}
return fp;
}
async function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLabelIDs, row) {
await loadFlatpickr();
const modal = document.getElementById('vikunja-edit-modal');
const titleInput = document.getElementById('vikunja-edit-title');
const dueDateInput = document.getElementById('vikunja-edit-due-date');
@ -117,18 +198,9 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
// Set current values
titleInput.value = title || '';
// Convert date format from "2006-01-02 15:04" to datetime-local format "2006-01-02T15:04"
if (dueDate) {
dueDateInput.value = dueDate.replace(' ', 'T');
} else {
dueDateInput.value = '';
}
if (reminderDate) {
reminderDateInput.value = reminderDate.replace(' ', 'T');
} else {
reminderDateInput.value = '';
}
// Initialize flatpickr
initFlatpickr(dueDateInput, dueDate);
initFlatpickr(reminderDateInput, reminderDate);
// Fetch and display labels
labelsContainer.innerHTML = '<p>Ładowanie etykiet...</p>';
@ -182,8 +254,8 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
async function saveTask() {
const newTitle = titleInput.value.trim();
const newDueDate = dueDateInput.value;
const newReminderDate = reminderDateInput.value;
const dueDateFP = dueDateInput._flatpickr.selectedDates[0];
const reminderDateFP = reminderDateInput._flatpickr.selectedDates[0];
// Get selected label IDs
const selectedLabels = Array.from(labelsContainer.querySelectorAll('input[type="checkbox"]:checked'))
@ -194,17 +266,15 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
return;
}
// Convert datetime-local format to RFC3339
// Convert to RFC3339
let formattedDueDate = '';
if (newDueDate) {
const date = new Date(newDueDate);
formattedDueDate = date.toISOString();
if (dueDateFP) {
formattedDueDate = dueDateFP.toISOString();
}
let formattedReminderDate = '';
if (newReminderDate) {
const date = new Date(newReminderDate);
formattedReminderDate = date.toISOString();
if (reminderDateFP) {
formattedReminderDate = reminderDateFP.toISOString();
}
try {
@ -315,7 +385,9 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
});
}
function openCreateModal(widgetID) {
async function openCreateModal(widgetID) {
await loadFlatpickr();
const modal = document.getElementById('vikunja-create-modal');
const titleInput = document.getElementById('vikunja-create-title');
const dueDateInput = document.getElementById('vikunja-create-due-date');
@ -325,8 +397,9 @@ function openCreateModal(widgetID) {
// Clear the form
titleInput.value = '';
dueDateInput.value = '';
reminderDateInput.value = '';
initFlatpickr(dueDateInput);
initFlatpickr(reminderDateInput);
// Fetch and populate projects
projectSelect.innerHTML = '<option value="">Ładowanie...</option>';
@ -400,8 +473,8 @@ function openCreateModal(widgetID) {
async function createTask() {
const title = titleInput.value.trim();
const dueDate = dueDateInput.value;
const reminderDate = reminderDateInput.value;
const dueDateFP = dueDateInput._flatpickr.selectedDates[0];
const reminderDateFP = reminderDateInput._flatpickr.selectedDates[0];
const projectID = projectSelect.value ? parseInt(projectSelect.value) : 0;
// Get selected label IDs
@ -415,15 +488,13 @@ function openCreateModal(widgetID) {
// Convert datetime-local format to RFC3339
let formattedDueDate = '';
if (dueDate) {
const date = new Date(dueDate);
formattedDueDate = date.toISOString();
if (dueDateFP) {
formattedDueDate = dueDateFP.toISOString();
}
let formattedReminderDate = '';
if (reminderDate) {
const date = new Date(reminderDate);
formattedReminderDate = date.toISOString();
if (reminderDateFP) {
formattedReminderDate = reminderDateFP.toISOString();
}
try {

@ -77,11 +77,25 @@
</div>
<div class="vikunja-form-group">
<label for="vikunja-create-due-date">Termin:</label>
<input type="datetime-local" id="vikunja-create-due-date" class="vikunja-input" step="60">
<div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-create-due-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
<div class="vikunja-form-group">
<label for="vikunja-create-reminder-date">Przypomnienie:</label>
<input type="datetime-local" id="vikunja-create-reminder-date" class="vikunja-input" step="60">
<div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-create-reminder-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
<div class="vikunja-form-group">
<label for="vikunja-create-project">Projekt:</label>
@ -117,11 +131,25 @@
</div>
<div class="vikunja-form-group">
<label for="vikunja-edit-due-date">Termin:</label>
<input type="datetime-local" id="vikunja-edit-due-date" class="vikunja-input" step="60">
<div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-edit-due-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
<div class="vikunja-form-group">
<label for="vikunja-edit-reminder-date">Przypomnienie:</label>
<input type="datetime-local" id="vikunja-edit-reminder-date" class="vikunja-input" step="60">
<div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-edit-reminder-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
<div class="vikunja-form-group">
<label>Etykiety:</label>