# ๐Ÿ“… HR Portal - ๅ€‹ไบบๅŒ–ๅŠŸ่ƒฝ่จญ่จˆ ## ๐ŸŽฏ ๅŠŸ่ƒฝๆฆ‚่ฟฐ ็‚บๆฏไฝๅ“กๅทฅๆไพ›ๅ€‹ไบบๅŒ–็š„ๅทฅไฝœๅ”ไฝœๅทฅๅ…ท: - ๐Ÿ“… **ๅ€‹ไบบ่กŒไบ‹ๆ›†** - ้กžไผผ Google Calendar - ๐ŸŽฅ **้›ฒ็ซฏๆœƒ่ญฐๅฎค** - ็ทšไธŠ่ฆ–่จŠๆœƒ่ญฐ - ๐Ÿ“ **ๅพ…่พฆไบ‹้ …** - ไปปๅ‹™็ฎก็† - ๐Ÿ“Š **ๅ€‹ไบบๅ„€่กจๆฟ** - ่ณ‡่จŠ็ธฝ่ฆฝ --- ## ๐Ÿ“… ่กŒไบ‹ๆ›†ๅŠŸ่ƒฝ (Calendar) ### ๆ ธๅฟƒๅŠŸ่ƒฝ #### 1. ๅ€‹ไบบ่กŒไบ‹ๆ›† - ๆ—ฅ/้€ฑ/ๆœˆ่ฆ–ๅœ– - ๅปบ็ซ‹ไบ‹ไปถ/ๆœƒ่ญฐ - ่จญๅฎšๆ้†’้€š็Ÿฅ - ้‡่ค‡ไบ‹ไปถ่จญๅฎš - ไบ‹ไปถๅˆ†้กž (ๅทฅไฝœใ€ๅ€‹ไบบใ€ๆœƒ่ญฐ็ญ‰) #### 2. ๅœ˜้šŠ่กŒไบ‹ๆ›† - ๆŸฅ็œ‹้ƒจ้–€ๅŒไบ‹็š„่กŒ็จ‹ - ๆ‰พๅ‡บๅ…ฑๅŒ็ฉบๆช” - ้ ็ด„ๆœƒ่ญฐๅฎค - ๆœƒ่ญฐ้‚€่ซ‹่ˆ‡ๅ›ž่ฆ† #### 3. ๆ•ดๅˆๅŠŸ่ƒฝ - **Google Calendar ๅŒๆญฅ** (้›™ๅ‘) - **Outlook Calendar ๅŒๆญฅ** (ๅฏ้ธ) - **iCal ๅŒฏๅ‡บ/ๅŒฏๅ…ฅ** - **้ƒตไปถๆ้†’** - **ๆ‰‹ๆฉŸ App ๆŽจ้€** --- ## ๐ŸŽฅ ้›ฒ็ซฏๆœƒ่ญฐๅฎคๅŠŸ่ƒฝ ### ๆ–นๆกˆ้ธๆ“‡ #### ๆ–นๆกˆ A: Jitsi Meet (้–‹ๆบ,่‡ชๆžถ) โญ ๆŽจ่–ฆ **ๅ„ช้ปž**: - โœ… ๅฎŒๅ…จๅ…่ฒป้–‹ๆบ - โœ… ๅฏ่‡ช่กŒ้ƒจ็ฝฒๅœจๆ‚จ็š„ไผบๆœๅ™จ - โœ… ็„กๆ™‚้–“้™ๅˆถ - โœ… ๆ”ฏๆด้Œ„ๅฝฑใ€่žขๅน•ๅˆ†ไบซ - โœ… ๅฏๆ•ดๅˆๅˆฐ HR Portal **้ƒจ็ฝฒๆ–นๅผ**: ```yaml # Docker Compose services: jitsi: image: jitsi/jitsi-meet ports: - "8443:443" environment: - ENABLE_AUTH=1 - ENABLE_GUESTS=0 labels: - "traefik.enable=true" - "traefik.http.routers.jitsi.rule=Host(`meet.porscheworld.tw`)" ``` **็ถฒๅ€**: https://meet.porscheworld.tw #### ๆ–นๆกˆ B: BigBlueButton (ๆ•™่‚ฒ/ไผๆฅญ็ดš) **ๅ„ช้ปž**: - โœ… ๅŠŸ่ƒฝๆ›ดๅฎŒๆ•ด (็™ฝๆฟใ€ๆŠ•็ฅจใ€ๅˆ†็ต„่จŽ่ซ–) - โœ… ้Œ„ๅฝฑ่‡ชๅ‹•่ฝ‰ๆช” - โœ… ๅญธ็ฟ’ๆ›ฒ็ทšไฝŽ **็ผบ้ปž**: - โš ๏ธ ่ณ‡ๆบ้œ€ๆฑ‚่ผƒ้ซ˜ - โš ๏ธ ่จญๅฎš่ผƒ่ค‡้›œ #### ๆ–นๆกˆ C: ๆ•ดๅˆ็ฌฌไธ‰ๆ–นๆœๅ‹™ - **Zoom API** - ้œ€่ฆไป˜่ฒปๅธณ่™Ÿ - **Google Meet** - ้œ€่ฆ Google Workspace - **Microsoft Teams** - ้œ€่ฆ Microsoft 365 --- ## ๐Ÿ—„๏ธ ่ณ‡ๆ–™ๅบซๆ“ดๅ……่จญ่จˆ ### 1. ่กŒไบ‹ๆ›†ไบ‹ไปถ่กจ (calendar_events) ```sql CREATE TABLE calendar_events ( id SERIAL PRIMARY KEY, employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE, -- ไบ‹ไปถ่ณ‡่จŠ title VARCHAR(200) NOT NULL, description TEXT, location VARCHAR(200), -- ๆ™‚้–“ start_time TIMESTAMP WITH TIME ZONE NOT NULL, end_time TIMESTAMP WITH TIME ZONE NOT NULL, all_day BOOLEAN DEFAULT FALSE, -- ้‡่ค‡่จญๅฎš is_recurring BOOLEAN DEFAULT FALSE, recurrence_rule VARCHAR(200), -- RRULE format (iCal standard) recurrence_end_date DATE, -- ๅˆ†้กž category VARCHAR(50) DEFAULT 'work', -- work, personal, meeting, holiday color VARCHAR(20) DEFAULT '#3788d8', -- ๆ้†’ reminder_minutes INTEGER, -- ๆๅ‰ๅนพๅˆ†้˜ๆ้†’ (15, 30, 60, 1440) -- ๆœƒ่ญฐ็›ธ้—œ is_meeting BOOLEAN DEFAULT FALSE, meeting_room_id INTEGER REFERENCES meeting_rooms(id), online_meeting_url VARCHAR(500), -- ็‹€ๆ…‹ status VARCHAR(20) DEFAULT 'confirmed', -- confirmed, tentative, cancelled -- ๅŒๆญฅ google_event_id VARCHAR(200), -- Google Calendar Event ID outlook_event_id VARCHAR(200), last_synced_at TIMESTAMP, -- ๅฏฉ่จˆ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_calendar_events_employee ON calendar_events(employee_id); CREATE INDEX idx_calendar_events_time ON calendar_events(start_time, end_time); CREATE INDEX idx_calendar_events_category ON calendar_events(category); ``` ### 2. ไบ‹ไปถๅƒ่ˆ‡่€…่กจ (event_attendees) ```sql CREATE TABLE event_attendees ( id SERIAL PRIMARY KEY, event_id INTEGER REFERENCES calendar_events(id) ON DELETE CASCADE, employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE, -- ๅ›ž่ฆ†็‹€ๆ…‹ status VARCHAR(20) DEFAULT 'pending', -- pending, accepted, declined, tentative -- ่ง’่‰ฒ role VARCHAR(20) DEFAULT 'attendee', -- organizer, attendee, optional -- ๆ˜ฏๅฆๅฟ…้ ˆ is_required BOOLEAN DEFAULT TRUE, -- ๅ›ž่ฆ†ๆ™‚้–“ responded_at TIMESTAMP, -- ๅ‚™่จป comment TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(event_id, employee_id) ); CREATE INDEX idx_event_attendees_event ON event_attendees(event_id); CREATE INDEX idx_event_attendees_employee ON event_attendees(employee_id); ``` ### 3. ๆœƒ่ญฐๅฎค่กจ (meeting_rooms) ```sql CREATE TABLE meeting_rooms ( id SERIAL PRIMARY KEY, -- ๆœƒ่ญฐๅฎค่ณ‡่จŠ name VARCHAR(100) NOT NULL, location VARCHAR(200), capacity INTEGER, -- ่จญๅ‚™ has_projector BOOLEAN DEFAULT FALSE, has_whiteboard BOOLEAN DEFAULT FALSE, has_video_conference BOOLEAN DEFAULT FALSE, equipment TEXT, -- JSON array -- ็ทšไธŠๆœƒ่ญฐๅฎค is_virtual BOOLEAN DEFAULT FALSE, jitsi_room_name VARCHAR(100), -- Jitsi ๅฐˆ็”จๆˆฟ้–“ๅ permanent_meeting_url VARCHAR(500), -- ็‹€ๆ…‹ is_active BOOLEAN DEFAULT TRUE, -- ้ ็ด„่จญๅฎš allow_booking BOOLEAN DEFAULT TRUE, booking_advance_days INTEGER DEFAULT 30, -- ๅฏๆๅ‰ๅคšๅฐ‘ๅคฉ้ ็ด„ min_booking_duration INTEGER DEFAULT 30, -- ๆœ€ๅฐ้ ็ด„ๆ™‚้–“(ๅˆ†้˜) max_booking_duration INTEGER DEFAULT 480, -- ๆœ€ๅคง้ ็ด„ๆ™‚้–“(ๅˆ†้˜) created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ๅˆๅง‹่ณ‡ๆ–™ INSERT INTO meeting_rooms (name, location, capacity, is_virtual, has_video_conference) VALUES ('ๅฏฆ้ซ”ๆœƒ่ญฐๅฎค A', '่พฆๅ…ฌๅฎค 3F', 10, FALSE, TRUE), ('ๅฏฆ้ซ”ๆœƒ่ญฐๅฎค B', '่พฆๅ…ฌๅฎค 5F', 6, FALSE, TRUE), ('็ทšไธŠๆœƒ่ญฐๅฎค 1', NULL, 50, TRUE, TRUE), ('็ทšไธŠๆœƒ่ญฐๅฎค 2', NULL, 50, TRUE, TRUE), ('็ทšไธŠๆœƒ่ญฐๅฎค 3', NULL, 50, TRUE, TRUE); ``` ### 4. ๅพ…่พฆไบ‹้ …่กจ (todos) ```sql CREATE TABLE todos ( id SERIAL PRIMARY KEY, employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE, -- ไปปๅ‹™่ณ‡่จŠ title VARCHAR(200) NOT NULL, description TEXT, -- ๅ„ชๅ…ˆ็ดš priority VARCHAR(20) DEFAULT 'medium', -- low, medium, high, urgent -- ๆ™‚้–“ due_date DATE, due_time TIME, -- ็‹€ๆ…‹ status VARCHAR(20) DEFAULT 'pending', -- pending, in-progress, completed, cancelled completed_at TIMESTAMP, -- ๅˆ†้กž category VARCHAR(50), tags TEXT[], -- PostgreSQL array -- ้—œ่ฏ related_project_id INTEGER REFERENCES projects(id), related_event_id INTEGER REFERENCES calendar_events(id), -- ๆŽ’ๅบ sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_todos_employee ON todos(employee_id); CREATE INDEX idx_todos_status ON todos(status); CREATE INDEX idx_todos_due_date ON todos(due_date); ``` ### 5. ้€š็Ÿฅ่กจ (notifications) ```sql CREATE TABLE notifications ( id SERIAL PRIMARY KEY, employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE, -- ้€š็Ÿฅๅ…งๅฎน title VARCHAR(200) NOT NULL, message TEXT, -- ้กžๅž‹ type VARCHAR(50) NOT NULL, -- calendar, meeting, todo, system, announcement -- ้—œ่ฏ่ณ‡ๆบ related_type VARCHAR(50), -- event, todo, project, employee related_id INTEGER, -- ๅ„ชๅ…ˆ็ดš priority VARCHAR(20) DEFAULT 'normal', -- low, normal, high -- ็‹€ๆ…‹ is_read BOOLEAN DEFAULT FALSE, read_at TIMESTAMP, -- ๅ‹•ไฝœ้€ฃ็ต action_url VARCHAR(500), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_notifications_employee ON notifications(employee_id); CREATE INDEX idx_notifications_read ON notifications(is_read); CREATE INDEX idx_notifications_created ON notifications(created_at DESC); ``` --- ## ๐Ÿ”Œ API ็ซฏ้ปž่จญ่จˆ ### ่กŒไบ‹ๆ›† API ``` GET /api/v1/calendar/events - ๅˆ—ๅ‡บไบ‹ไปถ (ๆ”ฏๆดๆ—ฅๆœŸ็ฏ„ๅœ้Žๆฟพ) GET /api/v1/calendar/events/:id - ๅ–ๅพ—ไบ‹ไปถ่ฉณๆƒ… POST /api/v1/calendar/events - ๅปบ็ซ‹ไบ‹ไปถ PUT /api/v1/calendar/events/:id - ๆ›ดๆ–ฐไบ‹ไปถ DELETE /api/v1/calendar/events/:id - ๅˆช้™คไบ‹ไปถ GET /api/v1/calendar/events/month/:year/:month - ๅ–ๅพ—ๆœˆๆ›† GET /api/v1/calendar/events/week/:date - ๅ–ๅพ—้€ฑๆ›† GET /api/v1/calendar/events/day/:date - ๅ–ๅพ—ๆ—ฅๆ›† POST /api/v1/calendar/events/:id/invite - ้‚€่ซ‹ๅƒ่ˆ‡่€… PUT /api/v1/calendar/events/:id/respond - ๅ›ž่ฆ†้‚€่ซ‹ (accept/decline/tentative) GET /api/v1/calendar/free-busy - ๆŸฅ่ฉข็ฉบ้–’ๆ™‚ๆฎต POST /api/v1/calendar/find-common-slot - ๅฐ‹ๆ‰พๅ…ฑๅŒ็ฉบๆช” POST /api/v1/calendar/sync/google - ๅŒๆญฅ Google Calendar GET /api/v1/calendar/export/ical - ๅŒฏๅ‡บ iCal ``` ### ๆœƒ่ญฐๅฎค API ``` GET /api/v1/meeting-rooms - ๅˆ—ๅ‡บๆœƒ่ญฐๅฎค GET /api/v1/meeting-rooms/:id - ๅ–ๅพ—ๆœƒ่ญฐๅฎค่ฉณๆƒ… POST /api/v1/meeting-rooms - ๅ‰ตๅปบๆœƒ่ญฐๅฎค (็ฎก็†ๅ“ก) PUT /api/v1/meeting-rooms/:id - ๆ›ดๆ–ฐๆœƒ่ญฐๅฎค DELETE /api/v1/meeting-rooms/:id - ๅˆช้™คๆœƒ่ญฐๅฎค GET /api/v1/meeting-rooms/:id/availability - ๆŸฅ่ฉขๅฏ็”จๆ™‚ๆฎต POST /api/v1/meeting-rooms/:id/book - ้ ็ด„ๆœƒ่ญฐๅฎค POST /api/v1/meetings/create-instant - ๅปบ็ซ‹ๅณๆ™‚ๆœƒ่ญฐ POST /api/v1/meetings/create-scheduled - ๅปบ็ซ‹้ ๅฎšๆœƒ่ญฐ GET /api/v1/meetings/:id/join-url - ๅ–ๅพ—ๆœƒ่ญฐๅŠ ๅ…ฅ้€ฃ็ต DELETE /api/v1/meetings/:id - ๅ–ๆถˆๆœƒ่ญฐ ``` ### ๅพ…่พฆไบ‹้ … API ``` GET /api/v1/todos - ๅˆ—ๅ‡บๅพ…่พฆไบ‹้ … GET /api/v1/todos/:id - ๅ–ๅพ—ๅพ…่พฆ่ฉณๆƒ… POST /api/v1/todos - ๅปบ็ซ‹ๅพ…่พฆ PUT /api/v1/todos/:id - ๆ›ดๆ–ฐๅพ…่พฆ DELETE /api/v1/todos/:id - ๅˆช้™คๅพ…่พฆ PUT /api/v1/todos/:id/complete - ๆจ™่จ˜ๅฎŒๆˆ PUT /api/v1/todos/:id/reorder - ้‡ๆ–ฐๆŽ’ๅบ GET /api/v1/todos/today - ไปŠๆ—ฅๅพ…่พฆ GET /api/v1/todos/overdue - ้€พๆœŸๅพ…่พฆ GET /api/v1/todos/upcoming - ๅณๅฐ‡ๅˆฐๆœŸ ``` ### ้€š็Ÿฅ API ``` GET /api/v1/notifications - ๅˆ—ๅ‡บ้€š็Ÿฅ GET /api/v1/notifications/unread - ๆœช่ฎ€้€š็Ÿฅ PUT /api/v1/notifications/:id/read - ๆจ™่จ˜ๅทฒ่ฎ€ PUT /api/v1/notifications/mark-all-read - ๅ…จ้ƒจๆจ™่จ˜ๅทฒ่ฎ€ DELETE /api/v1/notifications/:id - ๅˆช้™ค้€š็Ÿฅ ``` --- ## ๐ŸŽจ ๅ‰็ซฏไป‹้ข่จญ่จˆ ### 1. ๅ€‹ไบบๅ„€่กจๆฟ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ‘‹ ๆ—ฉๅฎ‰, Alice! ๐Ÿ”” (3) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ ๐Ÿ“… ไปŠๆ—ฅ่กŒ็จ‹ ๐Ÿ“ ๅพ…่พฆไบ‹้ … โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚ โ”‚ โ”‚ 09:00 - 10:00 โ”‚ โ”‚ โ˜ ๅฎŒๆˆๅ ฑๅ‘Š โ”‚โ”‚ โ”‚ โ”‚ ้ƒจ้–€้€ฑๆœƒ โ”‚ โ”‚ โ˜ ๅ›ž่ฆ†้ƒตไปถ โ”‚โ”‚ โ”‚ โ”‚ ๐Ÿ“ ๆœƒ่ญฐๅฎค A โ”‚ โ”‚ โ˜‘ ๅฏฉๆ ธๆ–‡ไปถ โ”‚โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚ โ”‚ โ”‚ 14:00 - 15:00 โ”‚ โ”‚ โ”‚ โ”‚ ๅฎขๆˆถ็ฐกๅ ฑ โ”‚ ๐ŸŽฅ ๅฟซ้€Ÿๆœƒ่ญฐ โ”‚ โ”‚ โ”‚ ๐ŸŽฅ ็ทšไธŠๆœƒ่ญฐ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ [ๅปบ็ซ‹ๆœƒ่ญฐๅฎค] โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ ๐Ÿ’พ ๅ„ฒๅญ˜็ฉบ้–“ ๐Ÿ“ง ้ƒต็ฎฑ็‹€ๆ…‹ โ”‚ โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘ 8.2/10GB โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 0.5/1GB โ”‚ โ”‚ โ”‚ โ”‚ ๐Ÿ” ๆˆ‘็š„็ณป็ตฑๆฌŠ้™ โ”‚ โ”‚ โ€ข Gitea โ€ข Webmail โ€ข HR Portal โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### 2. ่กŒไบ‹ๆ›†้ ้ข (ๆœˆ่ฆ–ๅœ–) ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ“… 2026ๅนด2ๆœˆ [ๆ—ฅ ้€ฑ ๆœˆ] [ไปŠๅคฉ]โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ ๆ—ฅ ไธ€ ไบŒ ไธ‰ ๅ›› ไบ” ๅ…ญ โ”‚ โ”‚ 1 2 โ”‚ โ”‚ 3 4 5 6 7 8 9 โ”‚ โ”‚ โ—ๆœƒ่ญฐ โ”‚ โ”‚ 10 11 12 13 14 15 16 โ”‚ โ”‚ โ—โ—ๅ‡บๅทฎ โ”‚ โ”‚ 17 18 19 20 21 22 23 โ”‚ โ”‚ โ—็ฐกๅ ฑ โ”‚ โ”‚ 24 25 26 27 28 โ”‚ โ”‚ โ”‚ โ”‚ [+ ๆ–ฐๅขžไบ‹ไปถ] [ๅŒๆญฅGoogle Calendar] โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### 3. ๅปบ็ซ‹ๆœƒ่ญฐๅฎคๅฐ่ฉฑๆก† ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐ŸŽฅ ๅปบ็ซ‹็ทšไธŠๆœƒ่ญฐ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ ๆœƒ่ญฐไธป้กŒ: ________________ โ”‚ โ”‚ โ”‚ โ”‚ ๆ—ฅๆœŸๆ™‚้–“: [2026-02-08] [14:00] โ”‚ โ”‚ โ”‚ โ”‚ ๆ™‚้•ท: [1ๅฐๆ™‚ โ–ผ] โ”‚ โ”‚ โ”‚ โ”‚ ๆœƒ่ญฐๅฎค: โ—‹ ่‡ชๅ‹•ๅˆ†้… โ”‚ โ”‚ โ—‹ ๆŒ‡ๅฎš: [ๆœƒ่ญฐๅฎค1 โ–ผ] โ”‚ โ”‚ โ”‚ โ”‚ ๅƒ่ˆ‡่€…: [ๆœๅฐ‹ๅ“กๅทฅ...] โ”‚ โ”‚ โ€ข Alice Wang โ”‚ โ”‚ โ€ข Bob Chen โ”‚ โ”‚ โ”‚ โ”‚ โ˜‘ ๅŒๆญฅๅˆฐ่กŒไบ‹ๆ›† โ”‚ โ”‚ โ˜‘ ็™ผ้€้ƒตไปถ้€š็Ÿฅ โ”‚ โ”‚ โ”‚ โ”‚ [ๅ–ๆถˆ] [ๅปบ็ซ‹ๆœƒ่ญฐ] โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## ๐Ÿ”„ ๆ•ดๅˆๆต็จ‹ ### Google Calendar ๅŒๆญฅๆต็จ‹ ``` HR Portal Google Calendar API โ”‚ โ”‚ โ”‚ 1. ๆŽˆๆฌŠ่ซ‹ๆฑ‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚ โ”‚ โ”‚ โ”‚ 2. OAuth Token โ”‚ โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ 3. ๅŒๆญฅไบ‹ไปถ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚ โ”‚ โ”‚ โ”‚ 4. ้›™ๅ‘ๅŒๆญฅ โ”‚ โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚ ``` ### ๆœƒ่ญฐๅฎค้ ็ด„ๆต็จ‹ ``` ็”จๆˆถๅปบ็ซ‹ๆœƒ่ญฐ โ†“ ้ธๆ“‡ๆœƒ่ญฐๅฎค/็ทšไธŠๆœƒ่ญฐ โ†“ ๆชขๆŸฅๅฏ็”จๆ€ง โ†“ ๅปบ็ซ‹ Jitsi ๆœƒ่ญฐๅฎค โ†“ ็”Ÿๆˆๆœƒ่ญฐ้€ฃ็ต โ†“ ็™ผ้€้‚€่ซ‹็ตฆๅƒ่ˆ‡่€… โ†“ ๅŠ ๅ…ฅ่กŒไบ‹ๆ›† โ†“ ็™ผ้€้ƒตไปถๆ้†’ ``` --- ## ๐Ÿ“ฑ ็งปๅ‹•็ซฏๆ”ฏๆด ### Progressive Web App (PWA) - ๅฎ‰่ฃๅˆฐๆ‰‹ๆฉŸไธป็•ซ้ข - ้›ข็ทšๆŸฅ็œ‹่กŒไบ‹ๆ›† - ๆŽจ้€้€š็Ÿฅ (ๆœƒ่ญฐๆ้†’) - ๅฟซ้€Ÿๅปบ็ซ‹ๅพ…่พฆไบ‹้ … --- ## ๐Ÿ”” ้€š็ŸฅๆฉŸๅˆถ ### ้€š็Ÿฅ้กžๅž‹ 1. **ๆœƒ่ญฐๆ้†’** - ๆœƒ่ญฐๅ‰ 15 ๅˆ†้˜ๆ้†’ - ๆกŒ้ข้€š็Ÿฅ + ้ƒตไปถ 2. **ๅพ…่พฆๅˆฐๆœŸ** - ๆˆชๆญขๆ—ฅๅ‰ 1 ๅคฉๆ้†’ - ็•ถๆ—ฅไธŠๅˆ 9 ้ปžๆ้†’ 3. **ๆœƒ่ญฐ้‚€่ซ‹** - ๅณๆ™‚้€š็Ÿฅ - ้ƒตไปถ้€š็Ÿฅ 4. **็ณป็ตฑๅ…ฌๅ‘Š** - ้‡่ฆ่จŠๆฏๆŽจ้€ --- ## ๐Ÿš€ ๅฏฆๆ–ฝ่จˆๅŠƒ ### Phase 1: ๅŸบ็คŽๅŠŸ่ƒฝ (2 ้€ฑ) - โœ… ่ณ‡ๆ–™ๅบซ Schema - โœ… ่กŒไบ‹ๆ›† CRUD API - โœ… ๅŸบๆœฌๅ‰็ซฏไป‹้ข ### Phase 2: ๆœƒ่ญฐๅฎคๆ•ดๅˆ (1 ้€ฑ) - โœ… Jitsi Meet ้ƒจ็ฝฒ - โœ… ๆœƒ่ญฐๅฎค้ ็ด„ API - โœ… ๆœƒ่ญฐ้€ฃ็ต็”Ÿๆˆ ### Phase 3: ้€ฒ้šŽๅŠŸ่ƒฝ (2 ้€ฑ) - โœ… Google Calendar ๅŒๆญฅ - โœ… ๅพ…่พฆไบ‹้ …็ฎก็† - โœ… ้€š็Ÿฅ็ณป็ตฑ ### Phase 4: ๅ„ชๅŒ– (1 ้€ฑ) - โœ… ๆ•ˆ่ƒฝๅ„ชๅŒ– - โœ… ็งปๅ‹•็ซฏ PWA - โœ… ไฝฟ็”จ่€…ๆธฌ่ฉฆ --- **้€™ๅฅ—ๅ€‹ไบบๅŒ–ๅŠŸ่ƒฝๅฐ‡ๅคงๅน…ๆๅ‡ๅ“กๅทฅ็š„ๅทฅไฝœๆ•ˆ็އ!** ๐ŸŽฏ