Thanks for the reply @mdi
I created a form the UI as in the image below,
And the value in the module field has as a connection to the custom doc i created
To how it should work, am able to create an appointment from that custom page that renders the calendar , it grays out the appointed slops.
In this am writing a client script
frappe.ui.form.on(‘Training Events’, {
onload: function(frm) {
frappe.pages['training-schedule'].on_page_load = function(wrapper) {
const page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Training Schedule',
single_column: true
});
$('<div id="training-calendar"></div>').appendTo(page.body).css({
marginTop: '20px'
});
function render_training_schedule() {
const calendarEl = document.getElementById('training-calendar');
const calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
selectable: true,
selectMirror: true,
selectLongPressDelay: 80,
editable: false,
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
// Fetch events from the server
events: function(fetchInfo, successCallback, failureCallback) {
frappe.call({
method: 'frappe.client.get_list',
args: {
doctype: 'Training Events',
fields: ['name', 'tittle', 'start', 'end', 'status'],
limit_page_length: 100
},
callback: function(r) {
if (r.message) {
let booked_dates = {};
const events = r.message.map(ev => {
const date = ev.start.split('T')[0];
booked_dates[date] = true;
return {
id: ev.name,
title: ev.tittle,
start: ev.start,
end: ev.end,
color: ev.status === 'Booked' ? 'gray' : ''
};
});
// Store booked dates on the calendar for validation
calendar.booked_dates = booked_dates;
successCallback(events);
}
}
});
},
select: function(info) {
const selectedDate = info.startStr.split('T')[0];
if (calendar.booked_dates && calendar.booked_dates[selectedDate]) {
frappe.msgprint(__('This day is already booked.'));
return;
}
frappe.prompt([
{
label: 'Title',
fieldname: 'tittle',
fieldtype: 'Data',
reqd: true
},
{
label: 'Your Email',
fieldname: 'created_by',
fieldtype: 'Link',
options: 'User',
reqd: true
},
{
label: 'Start Date',
fieldname: 'start',
fieldtype: 'Datetime',
reqd: true,
default: info.startStr
},
{
label: 'End Date',
fieldname: 'end',
fieldtype: 'Datetime',
reqd: true,
default: info.endStr
}
], function(values) {
// Step 1: Check for conflicts
frappe.call({
method: 'frappe.client.get_list',
args: {
doctype: 'Training Events',
filters: [
['status', '!=', 'Cancelled'],
['start', '<', values.end],
['end', '>', values.start]
],
fields: ['name', 'tittle', 'start', 'end']
},
callback: function(res) {
if (res.message && res.message.length > 0) {
frappe.msgprint(__('This timeslot is already booked.'));
} else {
// Step 2: If no conflicts, insert the event
frappe.call({
method: 'frappe.client.insert',
args: {
doc: {
doctype: 'Training Events',
tittle: values.tittle,
start: values.start,
end: values.end,
status: 'Booked'
}
},
callback: function() {
frappe.msgprint('Training event created.');
calendar.refetchEvents(); // Make sure you defined this calendar variable
}
});
}
}
});
}, 'Create Training Event', 'Create');
}
});
calendar.render();
}
let js = document.createElement('script');
js.src = 'https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js';
js.onload = render_training_schedule;
document.head.appendChild(js);
let css = document.createElement('link');
css.rel = 'stylesheet';
css.href = 'https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css';
document.head.appendChild(css);
frappe.require([], function() {
render_training_schedule();
});
};
}
});
Hope this explains it