@luckydye/calendar 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,31 +55,24 @@ export class CalendarViewElement extends LitElement {
55
55
  align-items: center;
56
56
  justify-content: space-between;
57
57
  padding: 8px 10px;
58
- background: var(--bg-secondary, rgba(36, 36, 38, 0.5));
59
- border: 1px solid var(--grid-color, rgba(255, 255, 255, 0.1));
58
+ background: var(--bg-primary, rgb(30, 30, 30));
60
59
  flex-shrink: 0;
61
60
  gap: 16px;
62
61
  position: absolute;
63
- bottom: .25rem;
64
- left: .5rem;
65
- right: 0.5rem;
62
+ bottom: 0;
63
+ left: 0;
64
+ right: 0;
66
65
  z-index: 100;
67
- border-radius: var(--border-radius-lg, 8px);
68
- overflow: hidden;
69
-
70
- --backdrop-padding: 30px;
71
- --backdrop-size: 42px;
72
66
  }
73
67
 
74
68
  .toolbar::before {
75
69
  content: '';
76
- width: 100%;
77
70
  position: absolute;
78
- top: calc(var(--backdrop-padding) * -1);
79
71
  left: 0;
80
- bottom: calc(var(--backdrop-padding) * -1);
81
- backdrop-filter: blur(var(--backdrop-size));
82
- z-index: -1;
72
+ right: 0;
73
+ bottom: 100%;
74
+ height: 48px;
75
+ background: linear-gradient(to bottom, transparent, var(--bg-primary, rgb(30, 30, 30)));
83
76
  pointer-events: none;
84
77
  }
85
78
 
@@ -460,13 +453,14 @@ export class CalendarViewElement extends LitElement {
460
453
  flex-direction: column;
461
454
  min-width: 0;
462
455
  max-height: 550px;
456
+ border-radius: var(--border-radius-lg, 8px);
457
+ overflow: hidden;
463
458
  }
464
459
 
465
460
  .event-detail-footer {
466
461
  border-top: 1px solid var(--grid-color, rgba(255, 255, 255, 0.1));
467
462
  padding: 12px 16px;
468
463
  background: var(--bg-elevated, rgba(20, 20, 20, 0.95));
469
- border-radius: 0 0 var(--border-radius-lg, 8px) 0;
470
464
  }
471
465
 
472
466
  .invite-response-buttons {
@@ -5229,16 +5223,20 @@ export class CalendarViewElement extends LitElement {
5229
5223
  </div>
5230
5224
 
5231
5225
  <div class="event-detail-body">
5232
- ${
5233
- event.location
5234
- ? html`
5226
+ ${(() => {
5227
+ const teamsMatch = event.description?.match(/https:\/\/teams\.microsoft\.com\/[^\s<>"]+/);
5228
+ const teamsUrl = teamsMatch ? teamsMatch[0] : null;
5229
+ if (!event.location && !teamsUrl) return null;
5230
+ return html`
5235
5231
  <div class="event-detail-section">
5236
5232
  <div class="event-detail-label">Location</div>
5237
- <div class="event-detail-value">${event.location}</div>
5233
+ <div class="event-detail-value">
5234
+ ${event.location ? html`<div>${event.location}</div>` : null}
5235
+ ${teamsUrl ? html`<a href="${teamsUrl}" class="event-detail-link" target="_blank" rel="noopener">Join Microsoft Teams Meeting</a>` : null}
5236
+ </div>
5238
5237
  </div>
5239
- `
5240
- : null
5241
- }
5238
+ `;
5239
+ })()}
5242
5240
 
5243
5241
  ${
5244
5242
  event.url
@@ -5477,70 +5475,8 @@ export class CalendarViewElement extends LitElement {
5477
5475
  render() {
5478
5476
  return html`
5479
5477
  <div class="container ${this.isDraggingFile ? "dragging-file" : ""}">
5480
- <div class="toolbar">
5481
- <div class="toolbar-left">
5482
- <slot name="toolbar-center"></slot>
5483
-
5484
- <button class="toolbar-button" title="Month" @click=${
5485
- this.scrollToMonth
5486
- }>
5487
- Month
5488
- </button>
5489
- <button class="toolbar-button" title="Today" @click=${
5490
- this.scrollToToday
5491
- }>
5492
- Today
5493
- </button>
5494
- <button ?disabled=${
5495
- this.historyStack.length === 0 ||
5496
- this.historyIndex >= this.historyStack.length - 1
5497
- } class="toolbar-button" title="Back" @click=${this.goBack}>
5498
-
5499
- </button>
5500
- <button ?disabled=${
5501
- this.historyStack.length === 0 || this.historyIndex === 0
5502
- } class="toolbar-button" title="Forward" @click=${this.goForward}>
5503
-
5504
- </button>
5505
- <div class="toolbar-zoom">
5506
- <input
5507
- type="range"
5508
- class="toolbar-zoom-slider"
5509
- min="${MIN_DAY_HEIGHT}"
5510
- max="${MAX_DAY_HEIGHT}"
5511
- .value=${this.dayHeight}
5512
- @input=${this.onZoomSliderChange}
5513
- title="Adjust zoom level"
5514
- />
5515
- </div>
5516
- </div>
5517
-
5518
- <div class="toolbar-right">
5519
- <!--<select
5520
- class="theme-selector"
5521
- .value=${this.currentTheme}
5522
- @change=${this.onThemeChange}
5523
- title="Select theme"
5524
- >
5525
- ${availableThemes.map(
5526
- (theme) =>
5527
- html`<option value="${theme.name}">${theme.label}</option>`,
5528
- )}
5529
- </select>-->
5530
- <div class="toolbar-search">
5531
- <span class="toolbar-search-icon">🔍</span>
5532
- <input
5533
- class="toolbar-search-input"
5534
- type="text"
5535
- placeholder="Filter events..."
5536
- .value=${this.filter}
5537
- @input=${this.onFilterInput}
5538
- />
5539
- </div>
5540
- </div>
5541
- </div>
5542
-
5543
5478
  <div class="body">
5479
+ <slot name="sidebar"></slot>
5544
5480
  <div class="calendar-area">
5545
5481
  <div class="canvas-layer">
5546
5482
  <canvas></canvas>
@@ -5560,6 +5496,67 @@ export class CalendarViewElement extends LitElement {
5560
5496
  ${this.minimapCanvas}
5561
5497
  ${this.renderEventDetail()}
5562
5498
  ${this.renderNotificationPopover()}
5499
+
5500
+ <div class="toolbar">
5501
+ <div class="toolbar-left">
5502
+ <button class="toolbar-button" title="Month" @click=${
5503
+ this.scrollToMonth
5504
+ }>
5505
+ Month
5506
+ </button>
5507
+ <button class="toolbar-button" title="Today" @click=${
5508
+ this.scrollToToday
5509
+ }>
5510
+ Today
5511
+ </button>
5512
+ <button ?disabled=${
5513
+ this.historyStack.length === 0 ||
5514
+ this.historyIndex >= this.historyStack.length - 1
5515
+ } class="toolbar-button" title="Back" @click=${this.goBack}>
5516
+
5517
+ </button>
5518
+ <button ?disabled=${
5519
+ this.historyStack.length === 0 || this.historyIndex === 0
5520
+ } class="toolbar-button" title="Forward" @click=${this.goForward}>
5521
+
5522
+ </button>
5523
+ <div class="toolbar-zoom">
5524
+ <input
5525
+ type="range"
5526
+ class="toolbar-zoom-slider"
5527
+ min="${MIN_DAY_HEIGHT}"
5528
+ max="${MAX_DAY_HEIGHT}"
5529
+ .value=${this.dayHeight}
5530
+ @input=${this.onZoomSliderChange}
5531
+ title="Adjust zoom level"
5532
+ />
5533
+ </div>
5534
+ </div>
5535
+
5536
+ <div class="toolbar-right">
5537
+ <!--<select
5538
+ class="theme-selector"
5539
+ .value=${this.currentTheme}
5540
+ @change=${this.onThemeChange}
5541
+ title="Select theme"
5542
+ >
5543
+ ${availableThemes.map(
5544
+ (theme) =>
5545
+ html`<option value="${theme.name}">${theme.label}</option>`,
5546
+ )}
5547
+ </select>-->
5548
+ <div class="toolbar-search">
5549
+ <span class="toolbar-search-icon">🔍</span>
5550
+ <input
5551
+ class="toolbar-search-input"
5552
+ type="text"
5553
+ placeholder="Filter events..."
5554
+ .value=${this.filter}
5555
+ @input=${this.onFilterInput}
5556
+ />
5557
+ </div>
5558
+ </div>
5559
+ </div>
5563
5560
  </div>
5564
5561
  </div>
5565
5562
  </div>
@@ -390,4 +390,17 @@ export class IndexedDBStorage implements CalendarStorage {
390
390
  transaction.onerror = () => reject(transaction.error);
391
391
  });
392
392
  }
393
+
394
+ async deleteEvent(id: string): Promise<void> {
395
+ const db = await this.open();
396
+
397
+ return new Promise((resolve, reject) => {
398
+ const transaction = db.transaction(this.storeName, "readwrite");
399
+ const store = transaction.objectStore(this.storeName);
400
+ store.delete(id);
401
+
402
+ transaction.oncomplete = () => resolve();
403
+ transaction.onerror = () => reject(transaction.error);
404
+ });
405
+ }
393
406
  }
@@ -484,6 +484,8 @@ export class InhouseBookingSource implements CalendarSource {
484
484
 
485
485
  /**
486
486
  * Delete a booking from the Inhouse Booking System.
487
+ * - Confirmed timetracks (id > 0): DELETE /timetracks/{id}?id=...&date=...&duration=...etc
488
+ * - Raw bookings (id <= 0): DELETE /timetracks/delete_bookings/{bookingId}
487
489
  */
488
490
  async deleteEvent(id: string): Promise<void> {
489
491
  if (!this.enabled) {
@@ -495,7 +497,34 @@ export class InhouseBookingSource implements CalendarSource {
495
497
  throw new Error(`Invalid event ID format: ${id}`);
496
498
  }
497
499
 
498
- await this.apiQueryRequest<unknown>(`/timetracks/delete_bookings/${parsedId.bookingId}`, 'DELETE', new URLSearchParams());
500
+ if (parsedId.id <= 0) {
501
+ await this.apiQueryRequest<unknown>(`/timetracks/delete_bookings/${parsedId.bookingId}`, 'DELETE', new URLSearchParams());
502
+ return;
503
+ }
504
+
505
+ const existingBookings = await this.fetchEvents();
506
+ const existing = existingBookings.find(e => e.id === id);
507
+ if (!existing) {
508
+ throw new Error(`Booking not found: ${id}`);
509
+ }
510
+
511
+ const durationMs = existing.end.getTime() - existing.start.getTime();
512
+ const duration = this.formatDuration(durationMs);
513
+ const date = existing.start.toISOString().split('T')[0];
514
+ const description = existing.description ?? '';
515
+
516
+ const params = new URLSearchParams({
517
+ id: parsedId.id.toString(),
518
+ unit_id: this.credentials.unitId || '',
519
+ employee_id: this.credentials.employeeId,
520
+ booking_id: parsedId.bookingId.toString(),
521
+ date,
522
+ description,
523
+ project_id: parsedId.projectId.toString(),
524
+ duration,
525
+ });
526
+
527
+ await this.apiQueryRequest<unknown>(`/timetracks/${parsedId.id}`, 'DELETE', params);
499
528
  }
500
529
 
501
530
  /**
package/src/app.css CHANGED
@@ -1,6 +1,22 @@
1
+ dialog {
2
+ background: var(--bg-tertiary);
3
+ }
4
+
5
+ .caldav-sidebar {
6
+ width: 250px;
7
+ flex-shrink: 0;
8
+ height: 100%;
9
+ border-right: 1px solid var(--grid-color, rgba(255, 255, 255, 0.1));
10
+ overflow: hidden;
11
+ transition: width 0.15s ease;
12
+ }
13
+
14
+ .caldav-sidebar.collapsed {
15
+ width: 36px;
16
+ }
17
+
1
18
  caldav-config {
2
19
  height: 100%;
3
- box-shadow: 0 0 24px rgba(0, 0, 0, 0.4);
4
20
  }
5
21
 
6
22
  .toolbar-button {