calendar-notes-plugin 1.0.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.
- package/.vscode/extensions.json +4 -0
- package/.vscode/launch.json +20 -0
- package/.vscode/mcp.json +9 -0
- package/.vscode/tasks.json +42 -0
- package/backend/policies/RLS.sql +1 -0
- package/backend/policies/delete.sql +6 -0
- package/backend/policies/insert.sql +6 -0
- package/backend/policies/select.sql +4 -0
- package/backend/policies/update.sql +8 -0
- package/backend/table.sql +9 -0
- package/calendar-notes-plugin/.editorconfig +17 -0
- package/calendar-notes-plugin/.hintrc +5 -0
- package/calendar-notes-plugin/.vscode/extensions.json +4 -0
- package/calendar-notes-plugin/.vscode/launch.json +20 -0
- package/calendar-notes-plugin/.vscode/mcp.json +9 -0
- package/calendar-notes-plugin/.vscode/tasks.json +42 -0
- package/calendar-notes-plugin/README.md +226 -0
- package/calendar-notes-plugin/angular.json +74 -0
- package/calendar-notes-plugin/package-lock.json +8917 -0
- package/calendar-notes-plugin/package.json +52 -0
- package/calendar-notes-plugin/public/favicon.ico +0 -0
- package/calendar-notes-plugin/public/widget.js +75 -0
- package/calendar-notes-plugin/src/app/app.config.ts +12 -0
- package/calendar-notes-plugin/src/app/app.css +0 -0
- package/calendar-notes-plugin/src/app/app.html +1 -0
- package/calendar-notes-plugin/src/app/app.routes.server.ts +8 -0
- package/calendar-notes-plugin/src/app/app.routes.ts +3 -0
- package/calendar-notes-plugin/src/app/app.spec.ts +23 -0
- package/calendar-notes-plugin/src/app/app.ts +17 -0
- package/calendar-notes-plugin/src/app/components/calender/calendar.css +73 -0
- package/calendar-notes-plugin/src/app/components/calender/calendar.html +26 -0
- package/calendar-notes-plugin/src/app/components/calender/calendar.spec.ts +23 -0
- package/calendar-notes-plugin/src/app/components/calender/calendar.ts +155 -0
- package/calendar-notes-plugin/src/app/components/floating-button/floating-button.css +17 -0
- package/calendar-notes-plugin/src/app/components/floating-button/floating-button.html +3 -0
- package/calendar-notes-plugin/src/app/components/floating-button/floating-button.spec.ts +23 -0
- package/calendar-notes-plugin/src/app/components/floating-button/floating-button.ts +25 -0
- package/calendar-notes-plugin/src/app/components/notes/notes.css +251 -0
- package/calendar-notes-plugin/src/app/components/notes/notes.html +107 -0
- package/calendar-notes-plugin/src/app/components/notes/notes.spec.ts +23 -0
- package/calendar-notes-plugin/src/app/components/notes/notes.ts +328 -0
- package/calendar-notes-plugin/src/app/components/panel/panel.css +28 -0
- package/calendar-notes-plugin/src/app/components/panel/panel.html +16 -0
- package/calendar-notes-plugin/src/app/components/panel/panel.spec.ts +23 -0
- package/calendar-notes-plugin/src/app/components/panel/panel.ts +46 -0
- package/calendar-notes-plugin/src/app/components/widget/widget.css +18 -0
- package/calendar-notes-plugin/src/app/components/widget/widget.html +17 -0
- package/calendar-notes-plugin/src/app/components/widget/widget.spec.ts +23 -0
- package/calendar-notes-plugin/src/app/components/widget/widget.ts +23 -0
- package/calendar-notes-plugin/src/app/services/api.spec.ts +16 -0
- package/calendar-notes-plugin/src/app/services/api.ts +39 -0
- package/calendar-notes-plugin/src/app/services/notification.spec.ts +16 -0
- package/calendar-notes-plugin/src/app/services/notification.ts +116 -0
- package/calendar-notes-plugin/src/backend/database/policies/delete.sql +6 -0
- package/calendar-notes-plugin/src/backend/database/policies/insert.sql +6 -0
- package/calendar-notes-plugin/src/backend/database/policies/select.sql +6 -0
- package/calendar-notes-plugin/src/backend/database/policies/update.sql +6 -0
- package/calendar-notes-plugin/src/backend/database/table.sql +13 -0
- package/calendar-notes-plugin/src/index.html +13 -0
- package/calendar-notes-plugin/src/main.ts +34 -0
- package/calendar-notes-plugin/src/styles.css +1 -0
- package/calendar-notes-plugin/tsconfig.app.json +17 -0
- package/calendar-notes-plugin/tsconfig.json +33 -0
- package/calendar-notes-plugin/tsconfig.spec.json +15 -0
- package/calendar-notes-plugin-1.0.0.tgz +0 -0
- package/package.json +29 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "notes-plugin",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"ng": "ng",
|
|
6
|
+
"start": "ng serve",
|
|
7
|
+
"build": "ng build",
|
|
8
|
+
"watch": "ng build --watch --configuration development",
|
|
9
|
+
"test": "ng test",
|
|
10
|
+
"serve:ssr:notes_plugin": "node dist/notes_plugin/server/server.mjs"
|
|
11
|
+
},
|
|
12
|
+
"prettier": {
|
|
13
|
+
"printWidth": 100,
|
|
14
|
+
"singleQuote": true,
|
|
15
|
+
"overrides": [
|
|
16
|
+
{
|
|
17
|
+
"files": "*.html",
|
|
18
|
+
"options": {
|
|
19
|
+
"parser": "angular"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"private": true,
|
|
25
|
+
"packageManager": "npm@11.6.2",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@angular/common": "^21.1.0",
|
|
28
|
+
"@angular/compiler": "^21.1.0",
|
|
29
|
+
"@angular/core": "^21.1.0",
|
|
30
|
+
"@angular/forms": "^21.1.0",
|
|
31
|
+
"@angular/platform-browser": "^21.1.0",
|
|
32
|
+
"@angular/platform-server": "^21.1.0",
|
|
33
|
+
"@angular/router": "^21.1.0",
|
|
34
|
+
"@angular/ssr": "^21.1.0",
|
|
35
|
+
"@supabase/supabase-js": "^2.95.3",
|
|
36
|
+
"express": "^5.1.0",
|
|
37
|
+
"rxjs": "~7.8.0",
|
|
38
|
+
"tslib": "^2.3.0",
|
|
39
|
+
"uuid": "^13.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@angular/build": "^21.1.0",
|
|
43
|
+
"@angular/cli": "^21.1.0",
|
|
44
|
+
"@angular/compiler-cli": "^21.1.0",
|
|
45
|
+
"@angular/elements": "^21.1.4",
|
|
46
|
+
"@types/express": "^5.0.1",
|
|
47
|
+
"@types/node": "^20.17.19",
|
|
48
|
+
"jsdom": "^27.1.0",
|
|
49
|
+
"typescript": "~5.9.2",
|
|
50
|
+
"vitest": "^4.0.8"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
|
|
3
|
+
if (document.getElementById("calendar-notes-widget")) return;
|
|
4
|
+
|
|
5
|
+
const config = window.CalendarNotesConfig || {};
|
|
6
|
+
|
|
7
|
+
const iframe = document.createElement("iframe");
|
|
8
|
+
|
|
9
|
+
iframe.id = "calendar-notes-widget";
|
|
10
|
+
iframe.src = "http://localhost:4200/";
|
|
11
|
+
|
|
12
|
+
iframe.style.position = "fixed";
|
|
13
|
+
iframe.style.bottom = "20px";
|
|
14
|
+
iframe.style.right = "20px";
|
|
15
|
+
iframe.style.width = "450px";
|
|
16
|
+
iframe.style.height = "650px";
|
|
17
|
+
iframe.style.border = "none";
|
|
18
|
+
iframe.style.zIndex = "999999";
|
|
19
|
+
iframe.style.background = "transparent";
|
|
20
|
+
|
|
21
|
+
document.body.appendChild(iframe);
|
|
22
|
+
|
|
23
|
+
window.addEventListener("message", async function (event) {
|
|
24
|
+
|
|
25
|
+
const data = event.data;
|
|
26
|
+
|
|
27
|
+
if (!data) return;
|
|
28
|
+
|
|
29
|
+
// LOAD NOTES
|
|
30
|
+
if (data.type === "LOAD_NOTES") {
|
|
31
|
+
|
|
32
|
+
const notes = await config.loadNotes?.();
|
|
33
|
+
|
|
34
|
+
iframe.contentWindow.postMessage({
|
|
35
|
+
type: "NOTES_LOADED",
|
|
36
|
+
notes
|
|
37
|
+
}, "*");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// SAVE NOTE
|
|
41
|
+
if (data.type === "SAVE_NOTE") {
|
|
42
|
+
|
|
43
|
+
const saved = await config.saveNote?.(data.payload);
|
|
44
|
+
|
|
45
|
+
iframe.contentWindow.postMessage({
|
|
46
|
+
type: "NOTE_SAVED",
|
|
47
|
+
note: saved
|
|
48
|
+
}, "*");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// UPDATE NOTE
|
|
52
|
+
if (data.type === "UPDATE_NOTE") {
|
|
53
|
+
|
|
54
|
+
const updated = await config.updateNote?.(data.payload);
|
|
55
|
+
|
|
56
|
+
iframe.contentWindow.postMessage({
|
|
57
|
+
type: "NOTE_UPDATED",
|
|
58
|
+
note: updated
|
|
59
|
+
}, "*");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// DELETE NOTE
|
|
63
|
+
if (data.type === "DELETE_NOTE") {
|
|
64
|
+
|
|
65
|
+
await config.deleteNote?.(data.id);
|
|
66
|
+
|
|
67
|
+
iframe.contentWindow.postMessage({
|
|
68
|
+
type: "NOTE_DELETED",
|
|
69
|
+
id: data.id
|
|
70
|
+
}, "*");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
})();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
|
|
2
|
+
import { provideRouter } from '@angular/router';
|
|
3
|
+
|
|
4
|
+
import { routes } from './app.routes';
|
|
5
|
+
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
|
|
6
|
+
|
|
7
|
+
export const appConfig: ApplicationConfig = {
|
|
8
|
+
providers: [
|
|
9
|
+
provideBrowserGlobalErrorListeners(),
|
|
10
|
+
provideRouter(routes), provideClientHydration(withEventReplay())
|
|
11
|
+
]
|
|
12
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<app-floating-button></app-floating-button>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
import { App } from './app';
|
|
3
|
+
|
|
4
|
+
describe('App', () => {
|
|
5
|
+
beforeEach(async () => {
|
|
6
|
+
await TestBed.configureTestingModule({
|
|
7
|
+
imports: [App],
|
|
8
|
+
}).compileComponents();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should create the app', () => {
|
|
12
|
+
const fixture = TestBed.createComponent(App);
|
|
13
|
+
const app = fixture.componentInstance;
|
|
14
|
+
expect(app).toBeTruthy();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should render title', async () => {
|
|
18
|
+
const fixture = TestBed.createComponent(App);
|
|
19
|
+
await fixture.whenStable();
|
|
20
|
+
const compiled = fixture.nativeElement as HTMLElement;
|
|
21
|
+
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, notes_plugin');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, signal, ViewEncapsulation } from '@angular/core';
|
|
3
|
+
import { FloatingButton } from './components/floating-button/floating-button';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'app-root',
|
|
7
|
+
standalone:true,
|
|
8
|
+
imports: [CommonModule,FloatingButton],
|
|
9
|
+
encapsulation: ViewEncapsulation.ShadowDom,
|
|
10
|
+
templateUrl: './app.html',
|
|
11
|
+
styleUrl: './app.css',
|
|
12
|
+
|
|
13
|
+
})
|
|
14
|
+
export class App {
|
|
15
|
+
protected readonly title = signal('notes_plugin');
|
|
16
|
+
|
|
17
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
.header {
|
|
2
|
+
display: flex;
|
|
3
|
+
justify-content: space-between;
|
|
4
|
+
align-items: center;
|
|
5
|
+
font-weight: 600;
|
|
6
|
+
font-size: 16px;
|
|
7
|
+
margin-bottom: 5px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.header button {
|
|
11
|
+
background: #f1f3f6;
|
|
12
|
+
border: none;
|
|
13
|
+
padding: 6px 10px;
|
|
14
|
+
border-radius: 8px;
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
transition: 0.2s ease;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.header button:hover {
|
|
20
|
+
background: #dfe6f3;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.calendar {
|
|
24
|
+
display: grid;
|
|
25
|
+
grid-template-columns: repeat(7, 1fr);
|
|
26
|
+
gap: 4px;
|
|
27
|
+
margin-bottom: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.day-name {
|
|
31
|
+
text-align: center;
|
|
32
|
+
font-size: 12px;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
color: #888;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.day {
|
|
38
|
+
text-align: center;
|
|
39
|
+
padding: 10px 0;
|
|
40
|
+
border-radius: 10px;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
transition: 0.2s ease;
|
|
43
|
+
font-size: 14px;
|
|
44
|
+
padding: 6px 0;
|
|
45
|
+
position: relative;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.day:hover {
|
|
49
|
+
background: #f0f4ff;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.day.selected {
|
|
53
|
+
background: #4f46e5;
|
|
54
|
+
color: white;
|
|
55
|
+
font-weight: bold;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.day.today {
|
|
59
|
+
border: 2px solid #4f46e5;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.day.empty {
|
|
63
|
+
cursor: default;
|
|
64
|
+
background: transparent;
|
|
65
|
+
}
|
|
66
|
+
.note-dot {
|
|
67
|
+
position: absolute;
|
|
68
|
+
bottom: 4px;
|
|
69
|
+
width: 6px;
|
|
70
|
+
height: 6px;
|
|
71
|
+
background-color: #ff5722;
|
|
72
|
+
border-radius: 50%;
|
|
73
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<div class="header">
|
|
2
|
+
<button (click)="prevMonth()">â—€</button>
|
|
3
|
+
<span>{{ currentDate | date: 'MMMM yyyy' }}</span>
|
|
4
|
+
<button (click)="nextMonth()">â–¶</button>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div class="calendar">
|
|
8
|
+
|
|
9
|
+
<div class="day-name" *ngFor="let d of ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']">
|
|
10
|
+
{{ d }}
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
class="day"
|
|
15
|
+
*ngFor="let day of calendarDays"
|
|
16
|
+
[class.empty]="!day"
|
|
17
|
+
[class.selected]="isSelected(day)"
|
|
18
|
+
[class.today]="isToday(day)"
|
|
19
|
+
(click)="day && selectDate(day)"
|
|
20
|
+
>
|
|
21
|
+
{{ day || '' }}
|
|
22
|
+
<span *ngIf="hasNotes(day)" class="note-dot"></span>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
</div>
|
|
26
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { Calendar } from './calendar';
|
|
4
|
+
|
|
5
|
+
describe('Calendar', () => {
|
|
6
|
+
let component: Calendar;
|
|
7
|
+
let fixture: ComponentFixture<Calendar>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [Calendar]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(Calendar);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
await fixture.whenStable();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'app-calendar',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
templateUrl: './calendar.html',
|
|
9
|
+
styleUrl: './calendar.css',
|
|
10
|
+
})
|
|
11
|
+
export class Calendar implements OnInit, OnChanges {
|
|
12
|
+
|
|
13
|
+
@Output() dateSelected = new EventEmitter<string>();
|
|
14
|
+
@Output() monthChanged = new EventEmitter<void>();
|
|
15
|
+
|
|
16
|
+
@Input() notes: { [key: string]: any[] } = {};
|
|
17
|
+
|
|
18
|
+
currentDate: Date = new Date();
|
|
19
|
+
calendarDays: (number | null)[] = [];
|
|
20
|
+
|
|
21
|
+
selectedDay: number | null = null;
|
|
22
|
+
|
|
23
|
+
today = new Date();
|
|
24
|
+
|
|
25
|
+
// INIT
|
|
26
|
+
|
|
27
|
+
ngOnInit() {
|
|
28
|
+
this.generateCalendar();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// INPUT CHANGES
|
|
32
|
+
|
|
33
|
+
ngOnChanges(changes: SimpleChanges) {
|
|
34
|
+
|
|
35
|
+
if (changes['notes']) {
|
|
36
|
+
// notes updated → refresh markers
|
|
37
|
+
this.generateCalendar();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
// CALENDAR GENERATION
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
generateCalendar() {
|
|
45
|
+
|
|
46
|
+
const year = this.currentDate.getFullYear();
|
|
47
|
+
const month = this.currentDate.getMonth();
|
|
48
|
+
|
|
49
|
+
const firstDay = new Date(year, month, 1).getDay();
|
|
50
|
+
const totalDays = new Date(year, month + 1, 0).getDate();
|
|
51
|
+
|
|
52
|
+
this.calendarDays = [];
|
|
53
|
+
|
|
54
|
+
// empty cells before first day
|
|
55
|
+
for (let i = 0; i < firstDay; i++) {
|
|
56
|
+
this.calendarDays.push(null);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// actual days
|
|
60
|
+
for (let day = 1; day <= totalDays; day++) {
|
|
61
|
+
this.calendarDays.push(day);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ----------------SELECT DATE----------------------
|
|
67
|
+
|
|
68
|
+
selectDate(day: number | null) {
|
|
69
|
+
|
|
70
|
+
if (!day) return;
|
|
71
|
+
|
|
72
|
+
this.selectedDay = day;
|
|
73
|
+
|
|
74
|
+
const dateString = this.formatDate(day);
|
|
75
|
+
|
|
76
|
+
this.dateSelected.emit(dateString);
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------DATE FORMAT------------------
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
formatDate(day: number): string {
|
|
84
|
+
|
|
85
|
+
const year = this.currentDate.getFullYear();
|
|
86
|
+
const month = String(this.currentDate.getMonth() + 1).padStart(2, '0');
|
|
87
|
+
const dayStr = String(day).padStart(2, '0');
|
|
88
|
+
|
|
89
|
+
return `${year}-${month}-${dayStr}`;
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ------------------MONTH NAVIGATION-------------------
|
|
94
|
+
|
|
95
|
+
nextMonth() {
|
|
96
|
+
|
|
97
|
+
this.currentDate = new Date(
|
|
98
|
+
this.currentDate.getFullYear(),
|
|
99
|
+
this.currentDate.getMonth() + 1,
|
|
100
|
+
1
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
this.selectedDay = null;
|
|
104
|
+
|
|
105
|
+
this.generateCalendar();
|
|
106
|
+
|
|
107
|
+
this.monthChanged.emit();
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
prevMonth() {
|
|
112
|
+
|
|
113
|
+
this.currentDate = new Date(
|
|
114
|
+
this.currentDate.getFullYear(),
|
|
115
|
+
this.currentDate.getMonth() - 1,
|
|
116
|
+
1
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
this.selectedDay = null;
|
|
120
|
+
|
|
121
|
+
this.generateCalendar();
|
|
122
|
+
|
|
123
|
+
this.monthChanged.emit();
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// -----------------UI HELPERS-------------------------
|
|
128
|
+
|
|
129
|
+
isSelected(day: number | null): boolean {
|
|
130
|
+
return day === this.selectedDay;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
isToday(day: number | null): boolean {
|
|
134
|
+
|
|
135
|
+
if (!day) return false;
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
day === this.today.getDate() &&
|
|
139
|
+
this.currentDate.getMonth() === this.today.getMonth() &&
|
|
140
|
+
this.currentDate.getFullYear() === this.today.getFullYear()
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
//------------------------ NOTES INDICATOR--------------------------
|
|
146
|
+
|
|
147
|
+
hasNotes(day: number | null): boolean {
|
|
148
|
+
|
|
149
|
+
if (!day) return false;
|
|
150
|
+
const date = this.formatDate(day);
|
|
151
|
+
return !!this.notes?.[date]?.length;
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.floating-btn {
|
|
2
|
+
position: fixed;
|
|
3
|
+
bottom: 20px;
|
|
4
|
+
right: 20px;
|
|
5
|
+
width: 60px;
|
|
6
|
+
height: 60px;
|
|
7
|
+
background:transparent;
|
|
8
|
+
border-radius: 10%;
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
cursor: pointer;
|
|
13
|
+
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
|
14
|
+
z-index: 999999;
|
|
15
|
+
font-size: 40px;
|
|
16
|
+
color: white;
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { FloatingButton } from './floating-button';
|
|
4
|
+
|
|
5
|
+
describe('FloatingButton', () => {
|
|
6
|
+
let component: FloatingButton;
|
|
7
|
+
let fixture: ComponentFixture<FloatingButton>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [FloatingButton]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(FloatingButton);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
await fixture.whenStable();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import { Panel } from '../panel/panel';
|
|
3
|
+
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { FormsModule } from '@angular/forms';
|
|
5
|
+
import { Notes } from '../notes/notes';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'app-floating-button',
|
|
9
|
+
imports: [CommonModule,FormsModule,Panel],
|
|
10
|
+
templateUrl: './floating-button.html',
|
|
11
|
+
styleUrl: './floating-button.css',
|
|
12
|
+
})
|
|
13
|
+
export class FloatingButton {
|
|
14
|
+
|
|
15
|
+
selectedDate: string = '';
|
|
16
|
+
isOpen = false;
|
|
17
|
+
|
|
18
|
+
toggle() {
|
|
19
|
+
this.isOpen = !this.isOpen;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onDateSelected(date: string) {
|
|
23
|
+
this.selectedDate = date;
|
|
24
|
+
}
|
|
25
|
+
}
|