`);
// Event listener for room name update
block.find('[name="room_name[]"]').keyup(function() {
block.find('.card-title').text($(this).val() || 'New Room');
});
return block;
};
$(document).ready(function(){
// --- Initial Load Logic ---
const roomsContainer = $('#rooms_container');
// Load existing/default data
if (Array.isArray(initial_rooms_data) && initial_rooms_data.length > 0) {
initial_rooms_data.forEach(room_data => {
const roomBlock = generateRoomBlock(room_data);
const tileContainer = roomBlock.find('.tile-blocks-container');
const tiles = Array.isArray(room_data.tiles) ? room_data.tiles : [];
tiles.forEach(tile_data => {
if (!tile_data.tile_placement) {
tile_data.tile_placement = 'floor';
}
const tileBlock = generateTileBlock(tile_data);
tileContainer.append(tileBlock);
tileBlock.find('[name="cost_basis[]"]').trigger('change');
});
roomsContainer.append(roomBlock);
updateRoomArea(roomBlock);
});
} else {
// Fallback for empty data
const defaultRoomData = {"room_name":"Living Room","length_ft":"","length_in":"","width_ft":"","width_in":"","height_ft":"","height_in":"","direct_area_sqft":"","direct_wall_area_sqft":"","floor_area":100,"wall_area":112,"tiles":[{"tile_name":"Standard Floor Tile","tile_placement":"floor","t_length_ft":"","t_length_in":"0","t_width_ft":"","t_width_in":"0","tile_area_sqft":1,"tiles_per_box":"","cost_basis":"per_box","tile_cost":""}]};
const defaultRoom = generateRoomBlock(defaultRoomData);
const defaultTile = generateTileBlock(defaultRoomData.tiles[0]);
defaultRoom.find('.tile-blocks-container').append(defaultTile);
roomsContainer.append(defaultRoom);
defaultTile.find('[name="cost_basis[]"]').trigger('change');
updateRoomArea(defaultRoom);
}
updateGlobalSummary(); // Final calculation refresh
// --- Dynamic Handlers ---
// 1. Add Room
$('#add_room_btn').click(function() {
const newRoomData = {
room_name: 'New Room ' + ($('.room-block').length + 1),
length_ft: '10', width_ft: '10', height_ft: '8',
direct_area_sqft: '',
direct_wall_area_sqft: '',
floor_area: 100.00,
wall_area: 112.00
};
const newRoom = generateRoomBlock(newRoomData);
const newTile = generateTileBlock({tile_placement: 'floor'});
newRoom.find('.tile-blocks-container').append(newTile);
roomsContainer.append(newRoom);
newTile.find('[name="cost_basis[]"]').trigger('change');
updateRoomArea(newRoom);
});
// 2a/2b. Add Tile buttons
roomsContainer.on('click', '.add_floor_tile_btn', function() {
const tileContainer = $(this).siblings('.tile-blocks-container');
const newTile = generateTileBlock({tile_placement: 'floor'});
tileContainer.append(newTile);
newTile.find('[name="cost_basis[]"]').trigger('change');
});
roomsContainer.on('click', '.add_wall_tile_btn', function() {
const tileContainer = $(this).siblings('.tile-blocks-container');
const newTile = generateTileBlock({tile_placement: 'wall'});
tileContainer.append(newTile);
newTile.find('[name="cost_basis[]"]').trigger('change');
});
// 3/4. Delete Room/Tile
roomsContainer.on('click', '.delete_room_btn', function() {
if ($('.room-block').length > 1) {
if (confirm("Are you sure you want to delete this room and all its tile calculations?")) {
$(this).closest('.room-block').remove();
updateGlobalSummary();
}
} else {
alert("You must have at least one room.");
}
});
roomsContainer.on('click', '.delete_tile_btn', function() {
const tileBlock = $(this).closest('.tile-block');
const roomBlock = tileBlock.closest('.room-block');
if (roomBlock.find('.tile-block').length > 1) {
if (confirm("Are you sure you want to delete this tile type?")) {
tileBlock.remove();
updateGlobalSummary();
}
} else {
alert("Each room must have at least one tile type specified.");
}
});
// 5. Dimension/Calculation Change (Master Listener)
roomsContainer.on('input change', '.calculate', function() {
const block = $(this).closest('.room-block');
// If the change is in a room dimension/area input (Direct Area, L, W, or H)
if (block.has(this) && ($(this).attr('name').includes('length') || $(this).attr('name').includes('width') || $(this).attr('name').includes('height') || $(this).attr('name').includes('direct_area_sqft') || $(this).attr('name').includes('direct_wall_area_sqft'))) {
updateRoomArea(block);
}
// If the change is in a tile dimension/cost input
if ($(this).closest('.tile-block').length) {
updateTileCalculations($(this).closest('.tile-block'));
}
});
// 6. Form Submission (COMPLETED BLOCK)
$('#order-form').submit(function(e){
e.preventDefault();
var _this = $(this);
var el = $('
').addClass('alert alert-danger p-1 rounded-0').hide();
_this.find('.alert').remove();
const rooms_data = [];
let validation_failed = false;
// --- Validation Loop & Data Aggregation ---
$('.room-block').each(function() {
if (validation_failed) return false;
const roomBlock = $(this);
const room_name = roomBlock.find('[name="room_name[]"]').val();
const direct_area = roomBlock.find('[name="direct_area_sqft[]"]').val();
const direct_wall_area = roomBlock.find('[name="direct_wall_area_sqft[]"]').val();
// 1. Room Name Validation
if (!room_name.trim()) {
alert("Please provide a name for all rooms.");
roomBlock.find('[name="room_name[]"]').focus();
validation_failed = true;
return false;
}
const room_l_ft = roomBlock.find('[name="length_ft[]"]').val();
const room_w_ft = roomBlock.find('[name="width_ft[]"]').val();
const room_h_ft = roomBlock.find('[name="height_ft[]"]').val();
// 2. Floor Area Validation
if(parseFloat(direct_area) <= 0 && (!room_l_ft || parseFloat(room_l_ft) <= 0 || !room_w_ft || parseFloat(room_w_ft) <= 0)) {
alert(`Please enter a Direct Floor Area or valid Length (ft) and Width (ft) for room: ${room_name}`);
roomBlock.find('[name="direct_area_sqft[]"]').focus();
validation_failed = true;
return false;
}
// 3. Wall Area Validation: If wall tiles are requested, ensure area calculation data exists.
const hasWallTiles = roomBlock.find('[name="tile_placement[]"][value="wall"]').length > 0;
if (hasWallTiles && parseFloat(direct_wall_area) <= 0 && (!room_h_ft || parseFloat(room_h_ft) <= 0)) {
alert(`Room '${room_name}' requires wall area for wall tiles. Please enter a Direct Wall Area or a valid Height (ft).`);
roomBlock.find('[name="direct_wall_area_sqft[]"]').focus();
validation_failed = true;
return false;
}
// 4. Tile Data Aggregation
const room_tiles = [];
roomBlock.find('.tile-block').each(function() {
const tileBlock = $(this);
// Basic Tile Validation: Cost and Tiles per Box are required
if(!tileBlock.find('[name="tile_cost[]"]').val() || !tileBlock.find('[name="tiles_per_box[]"]').val()) {
alert(`Tile details are incomplete in room: ${room_name}. Please check the tile cost and boxes per tile.`);
validation_failed = true;
return false;
}
room_tiles.push({
// Dimensions and Costing inputs
tile_name: tileBlock.find('[name="tile_name[]"]').val(),
tile_placement: tileBlock.find('[name="tile_placement[]"]').val(),
t_length_ft: tileBlock.find('[name="t_length_ft[]"]').val(),
t_length_in: tileBlock.find('[name="t_length_in[]"]').val(),
t_width_ft: tileBlock.find('[name="t_width_ft[]"]').val(),
t_width_in: tileBlock.find('[name="t_width_in[]"]').val(),
tiles_per_box: tileBlock.find('[name="tiles_per_box[]"]').val(),
cost_basis: tileBlock.find('[name="cost_basis[]"]').val(),
tile_cost: tileBlock.find('[name="tile_cost[]"]').val(),
// Calculated data (stored in data attributes)
tile_area_sqft: tileBlock.data('tile_area_sqft'),
total_boxes: tileBlock.data('total_boxes'),
total_cost: tileBlock.data('total_cost'),
total_tiles: tileBlock.data('total_tiles'),
});
});
if (validation_failed) return false;
// 5. Aggregated Room Data Push
rooms_data.push({
room_name: room_name,
// Dimension Inputs
length_ft: roomBlock.find('[name="length_ft[]"]').val(),
length_in: roomBlock.find('[name="length_in[]"]').val(),
width_ft: roomBlock.find('[name="width_ft[]"]').val(),
width_in: roomBlock.find('[name="width_in[]"]').val(),
height_ft: roomBlock.find('[name="height_ft[]"]').val(),
height_in: roomBlock.find('[name="height_in[]"]').val(),
// Direct Area Inputs (NEW and existing)
direct_area_sqft: direct_area,
direct_wall_area_sqft: direct_wall_area,
// Calculated Areas
floor_area: roomBlock.data('floor_area'),
wall_area: roomBlock.data('wall_area'),
// Tiles Array
tiles: room_tiles
});
});
if (validation_failed) return false;
// 7. Finalize Data for AJAX
// Move the aggregated complex data into the hidden form field
$('#hidden_rooms_data').val(JSON.stringify(rooms_data));
// 8. Send Data via AJAX
_this.find('button').attr('disabled', true);
$.ajax({
url:'../classes/Master.php?f=save_order',
data: new FormData($(this)[0]),
cache: false,
contentType: false,
processData: false,
method: 'POST',
type: 'POST',
dataType: 'json',
error:err=>{
console.log(err)
// Use 'start_load()' and 'end_load()' if defined in your system
alert("An error occurred. Check the console for details.");
_this.find('button').attr('disabled', false);
},
success:function(resp){
if(typeof resp =='object' && resp.status == 'success'){
// Redirect on success
location.replace("./?page=tiles/view_order&id="+resp.id)
}else if(resp.status == 'failed' && resp.msg != ''){
el.text(resp.msg)
_this.prepend(el)
$('html, body').animate({scrollTop: _this.offset().top},'fast');
}else{
alert("An unknown error occurred on the server.");
console.log(resp)
}
_this.find('button').attr('disabled', false);
}
})
})
});