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.
Files changed (66) hide show
  1. package/.vscode/extensions.json +4 -0
  2. package/.vscode/launch.json +20 -0
  3. package/.vscode/mcp.json +9 -0
  4. package/.vscode/tasks.json +42 -0
  5. package/backend/policies/RLS.sql +1 -0
  6. package/backend/policies/delete.sql +6 -0
  7. package/backend/policies/insert.sql +6 -0
  8. package/backend/policies/select.sql +4 -0
  9. package/backend/policies/update.sql +8 -0
  10. package/backend/table.sql +9 -0
  11. package/calendar-notes-plugin/.editorconfig +17 -0
  12. package/calendar-notes-plugin/.hintrc +5 -0
  13. package/calendar-notes-plugin/.vscode/extensions.json +4 -0
  14. package/calendar-notes-plugin/.vscode/launch.json +20 -0
  15. package/calendar-notes-plugin/.vscode/mcp.json +9 -0
  16. package/calendar-notes-plugin/.vscode/tasks.json +42 -0
  17. package/calendar-notes-plugin/README.md +226 -0
  18. package/calendar-notes-plugin/angular.json +74 -0
  19. package/calendar-notes-plugin/package-lock.json +8917 -0
  20. package/calendar-notes-plugin/package.json +52 -0
  21. package/calendar-notes-plugin/public/favicon.ico +0 -0
  22. package/calendar-notes-plugin/public/widget.js +75 -0
  23. package/calendar-notes-plugin/src/app/app.config.ts +12 -0
  24. package/calendar-notes-plugin/src/app/app.css +0 -0
  25. package/calendar-notes-plugin/src/app/app.html +1 -0
  26. package/calendar-notes-plugin/src/app/app.routes.server.ts +8 -0
  27. package/calendar-notes-plugin/src/app/app.routes.ts +3 -0
  28. package/calendar-notes-plugin/src/app/app.spec.ts +23 -0
  29. package/calendar-notes-plugin/src/app/app.ts +17 -0
  30. package/calendar-notes-plugin/src/app/components/calender/calendar.css +73 -0
  31. package/calendar-notes-plugin/src/app/components/calender/calendar.html +26 -0
  32. package/calendar-notes-plugin/src/app/components/calender/calendar.spec.ts +23 -0
  33. package/calendar-notes-plugin/src/app/components/calender/calendar.ts +155 -0
  34. package/calendar-notes-plugin/src/app/components/floating-button/floating-button.css +17 -0
  35. package/calendar-notes-plugin/src/app/components/floating-button/floating-button.html +3 -0
  36. package/calendar-notes-plugin/src/app/components/floating-button/floating-button.spec.ts +23 -0
  37. package/calendar-notes-plugin/src/app/components/floating-button/floating-button.ts +25 -0
  38. package/calendar-notes-plugin/src/app/components/notes/notes.css +251 -0
  39. package/calendar-notes-plugin/src/app/components/notes/notes.html +107 -0
  40. package/calendar-notes-plugin/src/app/components/notes/notes.spec.ts +23 -0
  41. package/calendar-notes-plugin/src/app/components/notes/notes.ts +328 -0
  42. package/calendar-notes-plugin/src/app/components/panel/panel.css +28 -0
  43. package/calendar-notes-plugin/src/app/components/panel/panel.html +16 -0
  44. package/calendar-notes-plugin/src/app/components/panel/panel.spec.ts +23 -0
  45. package/calendar-notes-plugin/src/app/components/panel/panel.ts +46 -0
  46. package/calendar-notes-plugin/src/app/components/widget/widget.css +18 -0
  47. package/calendar-notes-plugin/src/app/components/widget/widget.html +17 -0
  48. package/calendar-notes-plugin/src/app/components/widget/widget.spec.ts +23 -0
  49. package/calendar-notes-plugin/src/app/components/widget/widget.ts +23 -0
  50. package/calendar-notes-plugin/src/app/services/api.spec.ts +16 -0
  51. package/calendar-notes-plugin/src/app/services/api.ts +39 -0
  52. package/calendar-notes-plugin/src/app/services/notification.spec.ts +16 -0
  53. package/calendar-notes-plugin/src/app/services/notification.ts +116 -0
  54. package/calendar-notes-plugin/src/backend/database/policies/delete.sql +6 -0
  55. package/calendar-notes-plugin/src/backend/database/policies/insert.sql +6 -0
  56. package/calendar-notes-plugin/src/backend/database/policies/select.sql +6 -0
  57. package/calendar-notes-plugin/src/backend/database/policies/update.sql +6 -0
  58. package/calendar-notes-plugin/src/backend/database/table.sql +13 -0
  59. package/calendar-notes-plugin/src/index.html +13 -0
  60. package/calendar-notes-plugin/src/main.ts +34 -0
  61. package/calendar-notes-plugin/src/styles.css +1 -0
  62. package/calendar-notes-plugin/tsconfig.app.json +17 -0
  63. package/calendar-notes-plugin/tsconfig.json +33 -0
  64. package/calendar-notes-plugin/tsconfig.spec.json +15 -0
  65. package/calendar-notes-plugin-1.0.0.tgz +0 -0
  66. package/package.json +29 -0
@@ -0,0 +1,28 @@
1
+ .panel {
2
+ width: 380px;
3
+ max-height: 520px;
4
+ background: #ffffff;
5
+ border-radius: 18px;
6
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
7
+ padding: 12px;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 0;
11
+ overflow: hidden;
12
+ font-family: 'Segoe UI', sans-serif;
13
+ position: fixed; /* VERY IMPORTANT */
14
+ bottom: 90px; /* slightly above button */
15
+ right: 20px;
16
+ overflow-x: hidden;
17
+ height: 520px;
18
+ }
19
+
20
+ app-calendar {
21
+ flex-shrink: 0; /* calendar keeps its natural height */
22
+ }
23
+
24
+ app-notes {
25
+ display: flex;
26
+ flex: 1;
27
+ min-height: 0;
28
+ }
@@ -0,0 +1,16 @@
1
+ <div class="panel">
2
+
3
+ <app-calendar
4
+ [notes]="notes"
5
+ (dateSelected)="onDateSelected($event)"
6
+ (monthChanged)="onMonthChanged()">
7
+ </app-calendar>
8
+
9
+
10
+
11
+ <app-notes
12
+ [selectedDate]="selectedDate"
13
+ (notesChanged)="onNotesChanged($event)">
14
+ </app-notes>
15
+
16
+ </div>
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { Panel } from './panel';
4
+
5
+ describe('Panel', () => {
6
+ let component: Panel;
7
+ let fixture: ComponentFixture<Panel>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [Panel]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(Panel);
16
+ component = fixture.componentInstance;
17
+ await fixture.whenStable();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,46 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component, OnInit } from '@angular/core';
3
+ import { Calendar } from '../calender/calendar';
4
+ import { Notes } from '../notes/notes';
5
+ import { ReminderService } from '../../services/notification';
6
+
7
+ @Component({
8
+ selector: 'app-panel',
9
+ standalone: true,
10
+ imports: [CommonModule, Calendar, Notes],
11
+ templateUrl: './panel.html',
12
+ styleUrl: './panel.css',
13
+ })
14
+ export class Panel implements OnInit {
15
+
16
+ constructor(private reminderService: ReminderService) {}
17
+
18
+ selectedDate: string | null = null;
19
+ notes: { [key: string]: any[] } = {};
20
+ showAddForm = false;
21
+
22
+ ngOnInit(): void {
23
+ this.reminderService.requestPermission();
24
+ const today = new Date();
25
+ this.selectedDate = today.toISOString().split('T')[0];
26
+
27
+ }
28
+
29
+ onDateSelected(date: string) {
30
+ this.selectedDate = date;
31
+ }
32
+
33
+ onMonthChanged() {
34
+ // when month changes we clear date
35
+ this.selectedDate = null;
36
+ }
37
+
38
+ onNotesChanged(updatedNotes: { [key: string]: any[] }) {
39
+ this.notes = { ...updatedNotes };
40
+
41
+ }
42
+ toggleAddForm() {
43
+ this.showAddForm = !this.showAddForm;
44
+ }
45
+
46
+ }
@@ -0,0 +1,18 @@
1
+ .panel {
2
+ position: fixed;
3
+ bottom: 90px;
4
+ right: 20px;
5
+ width: 350px;
6
+ height: 500px;
7
+ background: white;
8
+ border-radius: 16px;
9
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
10
+ overflow: hidden;
11
+
12
+ transform: translateX(120%);
13
+ transition: transform 0.3s ease;
14
+ }
15
+
16
+ .panel.open {
17
+ transform: translateX(0);
18
+ }
@@ -0,0 +1,17 @@
1
+ <div
2
+ class="panel"
3
+ [class.open]="isOpen">
4
+
5
+ <app-calendar
6
+ (dateSelected)="onDateSelected($event)">
7
+ </app-calendar>
8
+
9
+ <app-notes
10
+ [selectedDate]="selectedDate">
11
+ </app-notes>
12
+
13
+ </div>
14
+
15
+ <button class="floating-btn" (click)="toggle()">
16
+ 📅
17
+ </button>
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { Widget } from './widget';
4
+
5
+ describe('Widget', () => {
6
+ let component: Widget;
7
+ let fixture: ComponentFixture<Widget>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [Widget]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(Widget);
16
+ component = fixture.componentInstance;
17
+ await fixture.whenStable();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,23 @@
1
+ import { Component } from '@angular/core';
2
+ import { Calendar } from '../calender/calendar';
3
+ import { Notes } from '../notes/notes';
4
+
5
+ @Component({
6
+ selector: 'app-widget',
7
+ imports: [Calendar,Notes],
8
+ templateUrl: './widget.html',
9
+ styleUrl: './widget.css',
10
+ })
11
+ export class Widget {
12
+
13
+ isOpen = false;
14
+ selectedDate = '';
15
+
16
+ toggle() {
17
+ this.isOpen = !this.isOpen;
18
+ }
19
+
20
+ onDateSelected(date: string) {
21
+ this.selectedDate = date;
22
+ }
23
+ }
@@ -0,0 +1,16 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { Api } from './api';
4
+
5
+ describe('Api', () => {
6
+ let service: Api;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(Api);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });
@@ -0,0 +1,39 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http';
3
+
4
+ @Injectable({ providedIn: 'root' })
5
+ export class ApiService {
6
+
7
+ private config: any = {};
8
+
9
+ constructor(private http: HttpClient) {
10
+ if (typeof window !== 'undefined') {
11
+ this.config = (window as any).CalendarNotesPlugin || {};
12
+ }
13
+ }
14
+
15
+ getNotes(date: string) {
16
+ return this.http.get(
17
+ `${this.config.apiBaseUrl}/notes?date=${date}`
18
+ );
19
+ }
20
+
21
+ saveNote(date: string, content: string) {
22
+ return this.http.post(
23
+ `${this.config.apiBaseUrl}/notes`,
24
+ { date, content }
25
+ );
26
+ }
27
+
28
+ getDatesWithNotes() {
29
+ if (!this.config.apiBaseUrl) {
30
+ console.error('API Base URL not configured');
31
+ return [];
32
+ }
33
+
34
+ return this.http.get(
35
+ `${this.config.apiBaseUrl}/notes/dates`
36
+ );
37
+ }
38
+
39
+ }
@@ -0,0 +1,16 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { Notification } from './notification';
4
+
5
+ describe('Notification', () => {
6
+ let service: Notification;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(Notification);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });
@@ -0,0 +1,116 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+
4
+ @Injectable({
5
+ providedIn: 'root',
6
+ })
7
+ export class ReminderService {
8
+
9
+ reminders: any[] = [];
10
+ timeTick$ = new Subject<void>();
11
+
12
+ constructor() {
13
+
14
+ // check reminders every 30 seconds
15
+ setInterval(() => {
16
+
17
+ this.checkReminders();
18
+
19
+ // notify components to refresh UI
20
+ this.timeTick$.next();
21
+
22
+ }, 1000); // every second
23
+ }
24
+
25
+ // =========================
26
+ // PERMISSION
27
+ // =========================
28
+
29
+ requestPermission() {
30
+
31
+ if ('Notification' in window && Notification.permission !== 'granted') {
32
+ Notification.requestPermission();
33
+ }
34
+
35
+ }
36
+
37
+ // =========================
38
+ // ADD REMINDER
39
+ // =========================
40
+
41
+ addReminder(note: any) {
42
+
43
+ if (!note.reminder_at) return;
44
+
45
+ const exists = this.reminders.find(r => r.id === note.id);
46
+ if (exists) return;
47
+
48
+ const reminderTime = new Date(note.reminder_at).getTime();
49
+ const now = new Date().getTime();
50
+
51
+ this.reminders.push({
52
+ ...note,
53
+ triggered: reminderTime <= now
54
+ });
55
+
56
+ }
57
+
58
+ // =========================
59
+ // REMOVE REMINDER
60
+ // =========================
61
+
62
+ removeReminder(noteId: string) {
63
+
64
+ this.reminders =
65
+ this.reminders.filter(r => r.id !== noteId);
66
+
67
+ }
68
+
69
+ // =========================
70
+ // CHECK REMINDERS
71
+ // =========================
72
+
73
+ checkReminders() {
74
+
75
+ const now = new Date().getTime();
76
+
77
+ for (const reminder of this.reminders) {
78
+
79
+ if (!reminder.reminder_at) continue;
80
+
81
+ const reminderTime =
82
+ new Date(reminder.reminder_at).getTime();
83
+
84
+ if (reminderTime <= now && !reminder.triggered) {
85
+
86
+ this.showNotification(
87
+ reminder.title,
88
+ reminder.content
89
+ );
90
+
91
+ reminder.triggered = true;
92
+
93
+ }
94
+
95
+ }
96
+
97
+ }
98
+
99
+ // =========================
100
+ // SHOW NOTIFICATION
101
+ // =========================
102
+
103
+ showNotification(title: string, body: string) {
104
+
105
+ if (Notification.permission === 'granted') {
106
+
107
+ new Notification(title, {
108
+ body: body,
109
+ icon: 'assets/icon.png'
110
+ });
111
+
112
+ }
113
+
114
+ }
115
+
116
+ }
@@ -0,0 +1,6 @@
1
+ alter policy "Users can delete their own notes"
2
+ on "public"."notes"
3
+ to public
4
+ using (
5
+ (auth.uid() = user_id)
6
+ );
@@ -0,0 +1,6 @@
1
+ alter policy "Users can insert their own notes"
2
+ on "public"."notes"
3
+ to public
4
+ with check (
5
+ (auth.uid() = user_id)
6
+ );
@@ -0,0 +1,6 @@
1
+ alter policy "Users can view their own notes"
2
+ on "public"."notes"
3
+ to public
4
+ using (
5
+ (auth.uid() = user_id)
6
+ );
@@ -0,0 +1,6 @@
1
+ alter policy "Users can update their own notes"
2
+ on "public"."notes"
3
+ to public
4
+ using (
5
+ (auth.uid() = user_id)
6
+ );
@@ -0,0 +1,13 @@
1
+ create table public.notes(
2
+ id uuid primary key default uuid_generate_v4(),
3
+ user_id uuid ,
4
+ note_date date not null,
5
+ title text,
6
+ content text,
7
+ created_at timestamp with time zone default now(),
8
+ updated_at timestamp with time zone default now(),
9
+ reminder_at timestamp with time zone default now(),
10
+ notified boolean
11
+
12
+
13
+ );
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>NotesPlugin</title>
6
+ <base href="/">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
9
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
10
+ </head>
11
+ <body>
12
+ </body>
13
+ </html>
@@ -0,0 +1,34 @@
1
+ import { bootstrapApplication } from '@angular/platform-browser';
2
+ import { App } from './app/app';
3
+ import { appConfig } from './app/app.config';
4
+
5
+ // Wrap everything in IIFE so it doesn’t pollute global scope
6
+ (function () {
7
+
8
+ // Prevent duplicate injection
9
+ if (document.getElementById('notes-calendar-widget')) {
10
+ return;
11
+ }
12
+
13
+ // Create main container
14
+ const container = document.createElement('div');
15
+ container.id = 'notes-calendar-widget';
16
+
17
+ // Optional: style container (floating panel style)
18
+ container.style.position = 'fixed';
19
+ container.style.bottom = '20px';
20
+ container.style.right = '20px';
21
+ container.style.zIndex = '999999';
22
+ container.style.overflow = 'hidden';
23
+
24
+ document.body.appendChild(container);
25
+
26
+ // Create Angular root element inside container
27
+ const appRoot = document.createElement('app-root');
28
+ container.appendChild(appRoot);
29
+
30
+ // Bootstrap Angular
31
+ bootstrapApplication(App, appConfig)
32
+ .catch(err => console.error(err));
33
+
34
+ })();
@@ -0,0 +1 @@
1
+ /* You can add global styles to this file, and also import other style files */
@@ -0,0 +1,17 @@
1
+ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
+ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
+ {
4
+ "extends": "./tsconfig.json",
5
+ "compilerOptions": {
6
+ "outDir": "./out-tsc/app",
7
+ "types": [
8
+ "node"
9
+ ]
10
+ },
11
+ "include": [
12
+ "src/**/*.ts"
13
+ ],
14
+ "exclude": [
15
+ "src/**/*.spec.ts"
16
+ ]
17
+ }
@@ -0,0 +1,33 @@
1
+ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
+ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
+ {
4
+ "compileOnSave": false,
5
+ "compilerOptions": {
6
+ "strict": true,
7
+ "noImplicitOverride": true,
8
+ "noPropertyAccessFromIndexSignature": true,
9
+ "noImplicitReturns": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "skipLibCheck": true,
12
+ "isolatedModules": true,
13
+ "experimentalDecorators": true,
14
+ "importHelpers": true,
15
+ "target": "ES2022",
16
+ "module": "preserve"
17
+ },
18
+ "angularCompilerOptions": {
19
+ "enableI18nLegacyMessageIdFormat": false,
20
+ "strictInjectionParameters": true,
21
+ "strictInputAccessModifiers": true,
22
+ "strictTemplates": true
23
+ },
24
+ "files": [],
25
+ "references": [
26
+ {
27
+ "path": "./tsconfig.app.json"
28
+ },
29
+ {
30
+ "path": "./tsconfig.spec.json"
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,15 @@
1
+ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
+ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
+ {
4
+ "extends": "./tsconfig.json",
5
+ "compilerOptions": {
6
+ "outDir": "./out-tsc/spec",
7
+ "types": [
8
+ "vitest/globals"
9
+ ]
10
+ },
11
+ "include": [
12
+ "src/**/*.d.ts",
13
+ "src/**/*.spec.ts"
14
+ ]
15
+ }
Binary file
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "calendar-notes-plugin",
3
+ "version": "1.0.0",
4
+ "description": "Embeddable calendar notes plugin",
5
+ "keywords": [
6
+ "calendar",
7
+ "notes",
8
+ "widget",
9
+ "calendar-widget",
10
+ "notes-widget",
11
+ "angular-plugin",
12
+ "embeddable-widget"
13
+ ],
14
+ "homepage": "https://github.com/Raj-mastud/calendar-notes-plugin#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/Raj-mastud/calendar-notes-plugin/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Raj-mastud/calendar-notes-plugin.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": "Raj Mastud",
24
+ "type": "commonjs",
25
+ "main": "public/widget.js",
26
+ "scripts": {
27
+ "test": "echo \"No tests specified\""
28
+ }
29
+ }