این راهنما تقویمی برای رزرو اتاق‌های جلسه می‌سازد: یک خط زمانی با یک ردیف برای هر اتاق، رزروهایی که با کشیدن سر جایشان می‌گذارید، قاعده‌ای که نمی‌گذارد یک اتاق دو بار هم‌زمان رزرو شود و فایلی که رزرو را به هر تقویمی اضافه می‌کند. این راهنما از زمان‌بند پکیج @needless-ui/angular استفاده می‌کند که مثل همهٔ Needless UI مجوز MIT دارد؛ پس خط زمانی منابع برای هر اپی رایگان است.

اتاق‌ها و رزروها

اتاق‌ها منابع‌اند: هر کدام یک id، یک title و هر چیز دیگری در data دارد. رزروها رویدادهایی با زمان‌های سادهٔ ISO هستند، مثل 2026-10-23T09:00، که شناسهٔ اتاقشان در resource است. یک رزرو می‌تواند چند اتاق، یا چند نفر، را به شکل فهرست در بر بگیرد.

یک ردیف برای هر اتاق

هر دو را همراه با view="timeline" به nui-scheduler بدهید. dayStart و dayEnd روز را به ساعت‌هایی محدود می‌کنند که اتاق‌ها باز هستند، snapMinutes رزروها را در گام‌های ربع‌ساعتی جابه‌جا می‌کند و businessHours زمانی را که اتاق‌ها بسته‌اند سایه‌دار می‌کند. nuiSchedulerResource سرِ هر ردیف را رسم می‌کند.

booking.html
<nui-scheduler
[events]="bookings()"
[resources]="rooms"
view="timeline"
dayStart="8"
dayEnd="19"
snapMinutes="15"
[businessHours]="hours"
editable
selectable
[allow]="free"
(slotSelect)="book($event)"
(eventChange)="move($event)"
(rangeChange)="range.set($event)"
>
<ng-template nuiSchedulerResource let-room>{{ room.title }}</ng-template>
</nui-scheduler>
<button nuiButton type="button" (click)="download()">Add to calendar</button>

رزرو و جابه‌جایی

زمان‌بند هیچ‌وقت خودش داده‌های شما را تغییر نمی‌دهد: فقط کاری را که کاربران انجام می‌دهند گزارش می‌دهد. کشیدن روی ردیف یک اتاق زمانی را انتخاب می‌کند و slotSelect آن را همراه با اتاق گزارش می‌دهد. کشیدن یک رزرو، یا انتهایش، زمان‌های جدیدش را در eventChange گزارش می‌دهد و اگر به اتاق دیگری برده شده باشد، اتاق جدیدش را هم. سیگنال را به‌روز کنید تا خط زمانی هم به‌روز شود.

booking.ts
import { Component, signal } from '@angular/core';
import { NuiButton } from '@needless-ui/angular/button';
import {
NuiScheduler,
type NuiSchedulerChange,
type NuiSchedulerEvent,
NuiSchedulerResourceTemplate,
type NuiSchedulerSlot,
} from '@needless-ui/angular/scheduler';
import { downloadIcs } from './booking-ics';

@Component({
selector: 'app-bookings',
imports: [NuiButton, NuiScheduler, NuiSchedulerResourceTemplate],
templateUrl: './booking.html',
})
export class Bookings {
/** Rooms are resources; bookings are events, with their room in `resource`. */
protected readonly rooms = [
{ id: 'atlas', title: 'Atlas' },
{ id: 'boreal', title: 'Boreal' },
];
protected readonly bookings = signal<NuiSchedulerEvent[]>([]);
/** Open on weekdays from 8:00 to 19:00: other time is shaded. */
protected readonly hours = [{ days: [1, 2, 3, 4, 5], start: '08:00', end: '19:00' }];
/** The days on screen: load their bookings when it changes. */
protected readonly range = signal<{ start: string; end: string } | null>(null);

protected book({ start, end, resource }: NuiSchedulerSlot): void {
const booking = { id: crypto.randomUUID(), title: 'New booking', start, end, resource };
this.bookings.update((all) => [...all, booking]);
}

protected move({ event, start, end, resource }: NuiSchedulerChange): void {
this.bookings.update((all) =>
all.map((b) =>
b.id === event.id ? { ...b, start, end, resource: resource ?? b.resource } : b,
),
);
}

/** A room holds one booking at a time. */
protected readonly free = (time: NuiSchedulerChange | NuiSchedulerSlot): boolean => {
const id = 'event' in time ? time.event.id : null;
const room = 'event' in time ? (time.resource ?? time.event.resource) : time.resource;
return !this.bookings().some(
(b) => b.id !== id && b.resource === room && b.start < time.end && b.end > time.start,
);
};

protected download(): void {
downloadIcs(this.bookings(), 'bookings');
}
}

بدون تداخل رزرو

پیش از ساختن یا جابه‌جا کردن هر رزرو، از allow پرسیده می‌شود. وقتی false برگرداند، کشیدن قرمز دیده می‌شود و رها کردنش چیزی اضافه نمی‌کند، و با صفحه‌کلید، زمان‌بند اعلام می‌کند که آن زمان در دسترس نیست. این مثال رزروهای دیگر همان اتاق را بررسی می‌کند؛ روی سرورتان هم دوباره بررسی کنید، چون حرف آخر را سرور می‌زند.

بارگذاری روزهای روی صفحه

rangeChange اولین و آخرین روز نمایش‌داده‌شده را گزارش می‌دهد، یک بار در ابتدا و دوباره هر بار که کاربران به صفحهٔ قبل یا بعد می‌روند، تا فقط رزروهای همان روزها را بگیرید، با httpResource یا هر درخواست دیگری که می‌خواهید.

افزودن به تقویم

nuiSchedulerToIcs() رزروها را به‌صورت یک فایل iCalendar می‌نویسد که Google Calendar، Outlook و Apple Calendar واردش می‌کنند. nuiSchedulerFromIcs() چنین فایلی را می‌خواند و زمان‌ها را از منطقه‌های زمانی دیگر به منطقهٔ زمانی خواننده می‌برد.

booking-ics.ts
import { type NuiSchedulerEvent, nuiSchedulerToIcs } from '@needless-ui/angular/scheduler';

/** Saves events as an .ics file, which Google Calendar, Outlook and Apple Calendar import. */
export function downloadIcs(events: readonly NuiSchedulerEvent[], name: string): void {
const file = new Blob([nuiSchedulerToIcs(events, { name })], { type: 'text/calendar' });
const link = document.createElement('a');
link.href = URL.createObjectURL(file);
link.download = `${name}.ics`;
link.click();
URL.revokeObjectURL(link.href);
}

صفحه‌کلید و صفحه‌خوان‌ها

همهٔ این‌ها بدون ماوس هم کار می‌کند. خط زمانی فقط یک توقف Tab دارد: با کلیدهای جهت‌نما میان زمان‌ها و اتاق‌ها حرکت می‌کنید، با Shift و جهت‌نماها زمان را انتخاب می‌کنید و با Enter آن را رزرو می‌کنید. روی یک رزرو، با Alt و جهت‌نماها آن را به زمان یا اتاق دیگری می‌برید. هر رزرو دکمه‌ای است که نامش از عنوان، اتاق، روز و زمانش می‌آید.

برای اتاق‌های کنار هم در نمای روز، رزروهای تکرارشونده و همهٔ گزینه‌های دیگر، صفحهٔ زمان‌بند و مرجع API آن را ببینید.