Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32e6c844f1 | ||
|
|
ab34361c28 | ||
|
|
be7c0d84bd |
BIN
assets/pasted-20251204-015537-032cd7e6.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
assets/pasted-20251204-015710-55b153a5.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
assets/pasted-20251204-015859-ba057228.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
assets/pasted-20251204-015945-278d0108.png
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
assets/pasted-20251204-020809-fbe187c3.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
assets/pasted-20251204-021404-14f90d2d.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
assets/pasted-20251204-021710-6689afd9.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
assets/pasted-20251204-022151-982a3976.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
assets/pasted-20251204-022306-1a04c023.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
assets/pasted-20251204-022406-7fe85bc3.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
assets/pasted-20251204-022454-940b0a04.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
assets/pasted-20251204-022551-bbe9e793.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
assets/pasted-20251204-022908-1d2c298f.png
Normal file
|
After Width: | Height: | Size: 321 KiB |
BIN
assets/pasted-20251204-044954-e49cb3ce.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
assets/pasted-20251211-060813-1bfd24b7.png
Normal file
|
After Width: | Height: | Size: 418 KiB |
BIN
assets/pasted-20251211-061003-c6ec9aea.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
assets/pasted-20251211-061109-702062e6.png
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
assets/pasted-20251211-061208-2fdbda92.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
assets/pasted-20251211-061245-39ca0406.png
Normal file
|
After Width: | Height: | Size: 228 KiB |
BIN
assets/pasted-20251211-061329-25a6f76d.png
Normal file
|
After Width: | Height: | Size: 251 KiB |
52
auth.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT u.id, u.username, u.password, u.role_id, r.name as role_name
|
||||
FROM users u
|
||||
JOIN roles r ON u.role_id = r.id
|
||||
WHERE u.username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
// Authentication successful
|
||||
$stmt = $pdo->prepare("SELECT p.name
|
||||
FROM permissions p
|
||||
JOIN role_permissions rp ON p.id = rp.permission_id
|
||||
WHERE rp.role_id = ?");
|
||||
$stmt->execute([$user['role_id']]);
|
||||
$permissions = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
$_SESSION['user'] = [
|
||||
'id' => $user['id'],
|
||||
'username' => $user['username'],
|
||||
'role' => $user['role_name'],
|
||||
'role_id' => $user['role_id'],
|
||||
'permissions' => $permissions
|
||||
];
|
||||
unset($_SESSION['error']);
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
} else {
|
||||
// Authentication failed
|
||||
$_SESSION['error'] = 'Invalid username or password.';
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$_SESSION['error'] = 'Database error: ' . $e->getMessage();
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
// Redirect if accessed directly
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
39
db/migrations/001_create_customer_tables.sql
Normal file
@ -0,0 +1,39 @@
|
||||
CREATE TABLE IF NOT EXISTS `customer_applications` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`application_id` VARCHAR(255) NOT NULL UNIQUE,
|
||||
`customer_id` VARCHAR(255) NULL,
|
||||
`company_name` VARCHAR(255) NOT NULL,
|
||||
`company_website` VARCHAR(255) NULL,
|
||||
`company_phone` VARCHAR(255) NULL,
|
||||
`sales_owner` VARCHAR(255) NOT NULL,
|
||||
`payment_terms` VARCHAR(255) NOT NULL,
|
||||
`tags` TEXT NULL,
|
||||
`notes` TEXT NULL,
|
||||
`status` VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
|
||||
`created_by` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `customer_contacts` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`customer_application_id` INT NOT NULL,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`email` VARCHAR(255) NOT NULL,
|
||||
`phone` VARCHAR(255) NULL,
|
||||
`is_primary` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `customer_addresses` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`customer_application_id` INT NOT NULL,
|
||||
`address_type` VARCHAR(50) NOT NULL, -- e.g., 'BILLING', 'SHIPPING'
|
||||
`address_line_1` VARCHAR(255) NOT NULL,
|
||||
`address_line_2` VARCHAR(255) NULL,
|
||||
`city` VARCHAR(255) NOT NULL,
|
||||
`state` VARCHAR(255) NOT NULL,
|
||||
`postal_code` VARCHAR(50) NOT NULL,
|
||||
`country` VARCHAR(100) NOT NULL,
|
||||
FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
8
db/migrations/002_create_application_files_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS `application_files` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`customer_application_id` INT NOT NULL,
|
||||
`filename` VARCHAR(255) NOT NULL,
|
||||
`filepath` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
58
db/migrations/003_create_user_management_tables.sql
Normal file
@ -0,0 +1,58 @@
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`username` VARCHAR(255) NOT NULL UNIQUE,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`role_id` INT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `roles` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(255) NOT NULL UNIQUE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `permissions` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(255) NOT NULL UNIQUE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `role_permissions` (
|
||||
`role_id` INT NOT NULL,
|
||||
`permission_id` INT NOT NULL,
|
||||
PRIMARY KEY (`role_id`, `permission_id`),
|
||||
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`permission_id`) REFERENCES `permissions`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT IGNORE INTO `roles` (`name`) VALUES ('admin'), ('manager'), ('sales'), ('clerk');
|
||||
|
||||
INSERT IGNORE INTO `permissions` (`name`) VALUES
|
||||
('create_application'),
|
||||
('edit_application'),
|
||||
('approve_application'),
|
||||
('view_applications'),
|
||||
('delete_application'),
|
||||
('manage_users'),
|
||||
('manage_roles'),
|
||||
('upload_files'),
|
||||
('delete_files');
|
||||
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'admin'), id FROM permissions;
|
||||
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'manager'), p.id
|
||||
FROM permissions p
|
||||
WHERE p.name IN ('create_application', 'edit_application', 'approve_application', 'view_applications', 'delete_application', 'upload_files', 'delete_files');
|
||||
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'sales'), p.id
|
||||
FROM permissions p
|
||||
WHERE p.name IN ('create_application', 'edit_application', 'view_applications', 'upload_files', 'delete_files');
|
||||
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'clerk'), p.id
|
||||
FROM permissions p
|
||||
WHERE p.name = 'view_applications';
|
||||
|
||||
INSERT IGNORE INTO `users` (`username`, `password`, `role_id`) VALUES ('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', (SELECT id FROM roles WHERE name = 'admin')); -- password is 'password'
|
||||
13
db/migrations/004_add_approval_workflow_columns.sql
Normal file
@ -0,0 +1,13 @@
|
||||
ALTER TABLE customer_applications ADD COLUMN approval_level INT DEFAULT 1;
|
||||
ALTER TABLE customer_applications ADD COLUMN current_approver_role_id INT;
|
||||
CREATE TABLE IF NOT EXISTS application_approvals (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
application_id INT NOT NULL,
|
||||
approver_id INT NOT NULL,
|
||||
approval_level INT NOT NULL,
|
||||
status VARCHAR(255) NOT NULL,
|
||||
comments TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (application_id) REFERENCES customer_applications(id),
|
||||
FOREIGN KEY (approver_id) REFERENCES users(id)
|
||||
);
|
||||
45
db/migrations/005_create_approver_roles.sql
Normal file
@ -0,0 +1,45 @@
|
||||
INSERT IGNORE INTO `roles` (`name`) VALUES
|
||||
('Approver Level 1'),
|
||||
('Approver Level 2'),
|
||||
('Approver Level 3'),
|
||||
('Approver Level 4'),
|
||||
('Approver Level 5'),
|
||||
('Approver Level 6'),
|
||||
('Approver Level 7');
|
||||
|
||||
INSERT IGNORE INTO `permissions` (`name`) VALUES
|
||||
('approve_level_1'),
|
||||
('approve_level_2'),
|
||||
('approve_level_3'),
|
||||
('approve_level_4'),
|
||||
('approve_level_5'),
|
||||
('approve_level_6'),
|
||||
('approve_level_7');
|
||||
|
||||
-- Assign approve_level_1 permission to Approver Level 1 role
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'Approver Level 1'), (SELECT id FROM permissions WHERE name = 'approve_level_1');
|
||||
|
||||
-- Assign approve_level_2 permission to Approver Level 2 role
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'Approver Level 2'), (SELECT id FROM permissions WHERE name = 'approve_level_2');
|
||||
|
||||
-- Assign approve_level_3 permission to Approver Level 3 role
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'Approver Level 3'), (SELECT id FROM permissions WHERE name = 'approve_level_3');
|
||||
|
||||
-- Assign approve_level_4 permission to Approver Level 4 role
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'Approver Level 4'), (SELECT id FROM permissions WHERE name = 'approve_level_4');
|
||||
|
||||
-- Assign approve_level_5 permission to Approver Level 5 role
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'Approver Level 5'), (SELECT id FROM permissions WHERE name = 'approve_level_5');
|
||||
|
||||
-- Assign approve_level_6 permission to Approver Level 6 role
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'Approver Level 6'), (SELECT id FROM permissions WHERE name = 'approve_level_6');
|
||||
|
||||
-- Assign approve_level_7 permission to Approver Level 7 role
|
||||
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
|
||||
SELECT (SELECT id FROM roles WHERE name = 'Approver Level 7'), (SELECT id FROM permissions WHERE name = 'approve_level_7');
|
||||
31
db/migrations/006_add_trade_ref_bank_and_signature.sql
Normal file
@ -0,0 +1,31 @@
|
||||
-- Add new tables for trade references and bank details
|
||||
CREATE TABLE IF NOT EXISTS `customer_trade_references` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`customer_application_id` int(11) NOT NULL,
|
||||
`company_name` varchar(255) NOT NULL,
|
||||
`contact_person` varchar(255) DEFAULT NULL,
|
||||
`email` varchar(255) DEFAULT NULL,
|
||||
`phone` varchar(50) DEFAULT NULL,
|
||||
`address` text,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `customer_application_id` (`customer_application_id`),
|
||||
CONSTRAINT `customer_trade_references_ibfk_1` FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `customer_bank_details` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`customer_application_id` int(11) NOT NULL,
|
||||
`bank_name` varchar(255) DEFAULT NULL,
|
||||
`branch` varchar(255) DEFAULT NULL,
|
||||
`bsb_number` varchar(50) DEFAULT NULL,
|
||||
`account_number` varchar(50) DEFAULT NULL,
|
||||
`account_name` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `customer_application_id` (`customer_application_id`),
|
||||
CONSTRAINT `customer_bank_details_ibfk_1` FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Add columns for declaration and signature to customer_applications
|
||||
ALTER TABLE `customer_applications`
|
||||
ADD COLUMN `declaration_text` TEXT,
|
||||
ADD COLUMN `signature_path` VARCHAR(255);
|
||||
20
db/migrations/007_add_financial_credit_details.sql
Normal file
@ -0,0 +1,20 @@
|
||||
ALTER TABLE `customer_applications`
|
||||
ADD COLUMN `major_product` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `capital` DECIMAL(20, 2) DEFAULT NULL,
|
||||
ADD COLUMN `capital_currency` VARCHAR(10) DEFAULT NULL,
|
||||
ADD COLUMN `main_shareholders` TEXT DEFAULT NULL,
|
||||
ADD COLUMN `num_employees` INT DEFAULT NULL,
|
||||
ADD COLUMN `payment_terms_ar` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `pl_year` YEAR DEFAULT NULL,
|
||||
ADD COLUMN `net_sales` DECIMAL(20, 2) DEFAULT NULL,
|
||||
ADD COLUMN `net_income_margin` DECIMAL(20, 2) DEFAULT NULL,
|
||||
ADD COLUMN `net_income_margin_ratio` DECIMAL(5, 2) DEFAULT NULL,
|
||||
ADD COLUMN `sales_target_this_year` DECIMAL(20, 2) DEFAULT NULL,
|
||||
ADD COLUMN `sales_target_next_year` DECIMAL(20, 2) DEFAULT NULL,
|
||||
ADD COLUMN `sales_target_after_next` DECIMAL(20, 2) DEFAULT NULL,
|
||||
ADD COLUMN `credit_rank` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `credit_limit` DECIMAL(20, 2) DEFAULT NULL,
|
||||
ADD COLUMN `credit_research_status` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `credit_research_reason` TEXT DEFAULT NULL,
|
||||
ADD COLUMN `tax_rate_area` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `billing_type` VARCHAR(255) DEFAULT NULL;
|
||||
21
db/migrations/008_add_del_to_info.sql
Normal file
@ -0,0 +1,21 @@
|
||||
-- Add columns for DEL-TO INFORMATIONS
|
||||
ALTER TABLE `customer_applications`
|
||||
ADD COLUMN `del_to_code` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `delivery_abbreviation` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_customer_name` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_address_1` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_address_2` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_address_3` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_address_4` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_postcode` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_phone` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_area_code` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_transportation_code` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_stock_point_code` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_recipient_section` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_country_code` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_shipment_flag` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_transport_days` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_shipment_condition_category` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_transport_service_exist` VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN `del_to_shipment_condition_place` VARCHAR(255) DEFAULT NULL;
|
||||
29
db/migrations/009_add_sop_details.sql
Normal file
@ -0,0 +1,29 @@
|
||||
ALTER TABLE `customer_applications`
|
||||
ADD COLUMN `doc_req_do` VARCHAR(255),
|
||||
ADD COLUMN `doc_req_packing_list` VARCHAR(255),
|
||||
ADD COLUMN `doc_req_invoice` VARCHAR(255),
|
||||
ADD COLUMN `doc_req_export_permit` VARCHAR(255),
|
||||
ADD COLUMN `doc_req_po_do_inv` VARCHAR(255),
|
||||
ADD COLUMN `doc_req_do_inv` VARCHAR(255),
|
||||
ADD COLUMN `doc_req_others` TEXT,
|
||||
ADD COLUMN `pack_req_one_line_carton` VARCHAR(255),
|
||||
ADD COLUMN `pack_req_one_item_carton` VARCHAR(255),
|
||||
ADD COLUMN `pack_req_one_item_pocket` VARCHAR(255),
|
||||
ADD COLUMN `pack_req_thomson_label` VARCHAR(255),
|
||||
ADD COLUMN `pack_req_contents_label` VARCHAR(255),
|
||||
ADD COLUMN `pack_req_delivery_schedule` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_name` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_code` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_address` TEXT,
|
||||
ADD COLUMN `forwarder_contact_person` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_phone` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_fax` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_delivery_method` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_delivery_timings` VARCHAR(255),
|
||||
ADD COLUMN `forwarder_delivery_requirements` TEXT,
|
||||
ADD COLUMN `special_instructions_shipping_mark` VARCHAR(255),
|
||||
ADD COLUMN `special_instructions_fax_documents` VARCHAR(255),
|
||||
ADD COLUMN `special_instructions_details` TEXT,
|
||||
ADD COLUMN `special_instructions_attention_to` VARCHAR(255),
|
||||
ADD COLUMN `special_instructions_fax_number` VARCHAR(255),
|
||||
ADD COLUMN `remarks` TEXT;
|
||||
6
db/migrations/010_add_new_approval_roles.sql
Normal file
@ -0,0 +1,6 @@
|
||||
INSERT IGNORE INTO `roles` (`name`) VALUES
|
||||
('Sales Manager'),
|
||||
('General Manager'),
|
||||
('Managing Director'),
|
||||
('Accounts'),
|
||||
('IT');
|
||||
52
db/migrations/012_add_full_application_fields_split.sql
Normal file
@ -0,0 +1,52 @@
|
||||
-- New fields for customer_applications from Page 1
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `fax` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `gst_reg_no` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `company_reg_no` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `date_of_incorporation` DATE DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `country_of_incorporation` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `contact_person_designation` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `credit_terms` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `account_setup_ar_statement` BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `account_setup_dunning_letter` BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `account_setup_ap_payment` BOOLEAN DEFAULT FALSE;
|
||||
|
||||
-- New table for shareholder/director information from Page 2
|
||||
CREATE TABLE IF NOT EXISTS `shareholder_director_information` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`application_id` INT NOT NULL,
|
||||
`name` VARCHAR(255) DEFAULT NULL,
|
||||
`address` TEXT DEFAULT NULL,
|
||||
`perc_of_shareholding` VARCHAR(255) DEFAULT NULL,
|
||||
`contact_no` VARCHAR(255) DEFAULT NULL,
|
||||
FOREIGN KEY (`application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- New fields for customer_trade_references from Page 2
|
||||
ALTER TABLE `customer_trade_references` ADD COLUMN `telephone_no` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_trade_references` ADD COLUMN `fax_no` VARCHAR(255) DEFAULT NULL;
|
||||
|
||||
-- New fields for banker's information from Page 3
|
||||
ALTER TABLE `customer_bank_details` ADD COLUMN `address` TEXT DEFAULT NULL;
|
||||
ALTER TABLE `customer_bank_details` ADD COLUMN `swift_code` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_bank_details` ADD COLUMN `contact_person` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_bank_details` ADD COLUMN `telephone_no` VARCHAR(255) DEFAULT NULL;
|
||||
ALTER TABLE `customer_bank_details` ADD COLUMN `fax_no` VARCHAR(255) DEFAULT NULL;
|
||||
|
||||
|
||||
-- New table for financial information from Page 3
|
||||
CREATE TABLE IF NOT EXISTS `customer_financial_information` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`customer_application_id` INT NOT NULL,
|
||||
`latest_audited_financial_year` VARCHAR(255) DEFAULT NULL,
|
||||
`shareholder_equity` VARCHAR(255) DEFAULT NULL,
|
||||
`paid_up_capital` VARCHAR(255) DEFAULT NULL,
|
||||
`annual_turnover` VARCHAR(255) DEFAULT NULL,
|
||||
`net_profit_loss` VARCHAR(255) DEFAULT NULL,
|
||||
`currency` VARCHAR(50) DEFAULT NULL,
|
||||
FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Declaration and Authorisation from Page 3
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `declaration_name` TEXT DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `declaration_designation` TEXT DEFAULT NULL;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `declaration_date` DATE DEFAULT NULL;
|
||||
7
db/migrations/013_add_nature_of_business.sql
Normal file
@ -0,0 +1,7 @@
|
||||
ALTER TABLE `customer_applications` MODIFY `company_name` TEXT;
|
||||
ALTER TABLE `customer_applications` MODIFY `company_website` TEXT;
|
||||
ALTER TABLE `customer_applications` MODIFY `sales_owner` TEXT;
|
||||
ALTER TABLE `customer_applications` MODIFY `major_product` TEXT;
|
||||
ALTER TABLE `customer_applications` MODIFY `credit_rank` TEXT;
|
||||
ALTER TABLE `customer_applications` MODIFY `del_to_customer_name` TEXT;
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `nature_of_business` VARCHAR(255) DEFAULT NULL;
|
||||
1
db/migrations/014_add_credit_terms_requested.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `credit_terms_requested` VARCHAR(255) DEFAULT NULL;
|
||||
1
db/migrations/015_add_credit_limit_requested.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `customer_applications` ADD COLUMN `credit_limit_requested` DECIMAL(15, 2) DEFAULT NULL;
|
||||
1
db/migrations/016_add_nric_to_shareholders.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `shareholder_director_information` ADD COLUMN `nric_fin` VARCHAR(255) DEFAULT NULL;
|
||||
10
db/migrations/017_create_customer_principals_table.sql
Normal file
@ -0,0 +1,10 @@
|
||||
-- Create customer_principals table
|
||||
CREATE TABLE IF NOT EXISTS `customer_principals` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`customer_application_id` INT NOT NULL,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`designation` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
59
delete_file.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
redirect_if_not_authenticated();
|
||||
redirect_if_no_permission('delete_files');
|
||||
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$file_id = $_POST['file_id'] ?? null;
|
||||
$application_id = $_POST['application_id'] ?? null;
|
||||
|
||||
if (!$file_id || !$application_id) {
|
||||
$_SESSION['message'] = 'Invalid request.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// First, get the filename to delete it from the server
|
||||
$stmt = $pdo->prepare("SELECT stored_filename FROM application_files WHERE id = ? AND application_id = ?");
|
||||
$stmt->execute([$file_id, $application_id]);
|
||||
$file = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($file) {
|
||||
$filepath = __DIR__ . '/uploads/' . $file['stored_filename'];
|
||||
|
||||
// Delete the file from the filesystem
|
||||
if (file_exists($filepath)) {
|
||||
unlink($filepath);
|
||||
}
|
||||
|
||||
// Delete the record from the database
|
||||
$delete_stmt = $pdo->prepare("DELETE FROM application_files WHERE id = ?");
|
||||
$delete_stmt->execute([$file_id]);
|
||||
|
||||
$_SESSION['message'] = 'File deleted successfully.';
|
||||
$_SESSION['message_type'] = 'success';
|
||||
} else {
|
||||
$_SESSION['message'] = 'File not found or you do not have permission to delete it.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// In a real app, log this error
|
||||
$_SESSION['message'] = 'Database error while deleting file.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
}
|
||||
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
|
||||
} else {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
282
edit_application.php
Normal file
@ -0,0 +1,282 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
redirect_if_not_authenticated();
|
||||
redirect_if_no_permission('edit_application');
|
||||
|
||||
require_once 'db/config.php';
|
||||
|
||||
$application_id = $_GET['id'] ?? null;
|
||||
if (!$application_id) {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$customer = null;
|
||||
$contacts = [];
|
||||
$addresses = [];
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// Fetch customer
|
||||
$stmt = $pdo->prepare("SELECT * FROM customer_applications WHERE id = ?");
|
||||
$stmt->execute([$application_id]);
|
||||
$customer = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($customer) {
|
||||
// Fetch contacts
|
||||
$stmt = $pdo->prepare("SELECT * FROM customer_contacts WHERE customer_application_id = ? ORDER BY is_primary DESC, id ASC");
|
||||
$stmt->execute([$application_id]);
|
||||
$contacts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Fetch addresses
|
||||
$stmt = $pdo->prepare("SELECT * FROM customer_addresses WHERE customer_application_id = ? ORDER BY id ASC");
|
||||
$stmt->execute([$application_id]);
|
||||
$addresses = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Fetch files
|
||||
$stmt = $pdo->prepare("SELECT * FROM application_files WHERE customer_application_id = ? ORDER BY created_at DESC");
|
||||
$stmt->execute([$application_id]);
|
||||
$files = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
die("Database error: " . $e->getMessage());
|
||||
}
|
||||
|
||||
if (!$customer) {
|
||||
http_response_code(404);
|
||||
echo "Application not found.";
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Customer Application</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Customer Master</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="logout.php">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mt-4">
|
||||
<h2>Edit Customer Application #<?php echo $customer['id']; ?></h2>
|
||||
<form action="update_application.php" method="POST" id="applicationForm" enctype="multipart/form-data">
|
||||
<input type="hidden" name="customer_id" value="<?php echo $customer['id']; ?>">
|
||||
|
||||
<!-- Company Details -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Company Details</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="company_name" class="form-label">Company Name</label>
|
||||
<input type="text" class="form-control" id="company_name" name="company_name" value="<?php echo htmlspecialchars($customer['company_name']); ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="company_website" class="form-label">Company Website</label>
|
||||
<input type="url" class="form-control" id="company_website" name="company_website" value="<?php echo htmlspecialchars($customer['company_website']); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="company_phone" class="form-label">Company Phone</label>
|
||||
<input type="tel" class="form-control" id="company_phone" name="company_phone" value="<?php echo htmlspecialchars($customer['company_phone']); ?>">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="sales_owner" class="form-label">Sales Owner</label>
|
||||
<input type="text" class="form-control" id="sales_owner" name="sales_owner" value="<?php echo htmlspecialchars($customer['sales_owner']); ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="payment_terms" class="form-label">Payment Terms</label>
|
||||
<input type="text" class="form-control" id="payment_terms" name="payment_terms" value="<?php echo htmlspecialchars($customer['payment_terms']); ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="tags" class="form-label">Tags</label>
|
||||
<input type="text" class="form-control" id="tags" name="tags" value="<?php echo htmlspecialchars($customer['tags']); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="notes" class="form-label">Notes</label>
|
||||
<textarea class="form-control" id="notes" name="notes" rows="3"><?php echo htmlspecialchars($customer['notes']); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contacts -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
Contacts
|
||||
<button type="button" class="btn btn-sm btn-primary" id="addContact"><i class="bi bi-plus-circle"></i> Add Contact</button>
|
||||
</div>
|
||||
<div class="card-body" id="contactsContainer">
|
||||
<?php foreach ($contacts as $index => $contact): ?>
|
||||
<div class="contact-group border p-3 mb-3">
|
||||
<input type="hidden" name="contact[<?php echo $index; ?>][id]" value="<?php echo $contact['id']; ?>">
|
||||
<button type="button" class="btn-close float-end" aria-label="Close" onclick="if(confirm('Are you sure you want to delete this contact?')) { this.parentElement.remove(); }"></button>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="radio" name="contact[<?php echo $index; ?>][is_primary]" id="contact_<?php echo $index; ?>_is_primary" value="1" <?php echo $contact['is_primary'] ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="contact_<?php echo $index; ?>_is_primary">Primary Contact</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3"><input type="text" name="contact[<?php echo $index; ?>][name]" class="form-control" placeholder="Name" value="<?php echo htmlspecialchars($contact['name']); ?>" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="email" name="contact[<?php echo $index; ?>][email]" class="form-control" placeholder="Email" value="<?php echo htmlspecialchars($contact['email']); ?>" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="tel" name="contact[<?php echo $index; ?>][phone]" class="form-control" placeholder="Phone" value="<?php echo htmlspecialchars($contact['phone']); ?>"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Addresses -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
Addresses
|
||||
<button type="button" class="btn btn-sm btn-primary" id="addAddress"><i class="bi bi-plus-circle"></i> Add Address</button>
|
||||
</div>
|
||||
<div class="card-body" id="addressesContainer">
|
||||
<?php foreach ($addresses as $index => $address): ?>
|
||||
<div class="address-group border p-3 mb-3">
|
||||
<input type="hidden" name="address[<?php echo $index; ?>][id]" value="<?php echo $address['id']; ?>">
|
||||
<button type="button" class="btn-close float-end" aria-label="Close" onclick="if(confirm('Are you sure you want to delete this address?')) { this.parentElement.remove(); }"></button>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<select name="address[<?php echo $index; ?>][type]" class="form-select" required>
|
||||
<option value="BILLING" <?php echo $address['address_type'] === 'BILLING' ? 'selected' : ''; ?>>Billing</option>
|
||||
<option value="SHIPPING" <?php echo $address['address_type'] === 'SHIPPING' ? 'selected' : ''; ?>>Shipping</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3"><input type="text" name="address[<?php echo $index; ?>][line1]" class="form-control" placeholder="Address Line 1" value="<?php echo htmlspecialchars($address['street']); ?>" required></div>
|
||||
<div class="mb-3"><input type="text" name="address[<?php echo $index; ?>][line2]" class="form-control" placeholder="Address Line 2" value="<?php echo htmlspecialchars($address['street2']); ?>"></div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3"><input type="text" name="address[<?php echo $index; ?>][city]" class="form-control" placeholder="City" value="<?php echo htmlspecialchars($address['city']); ?>" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="text" name="address[<?php echo $index; ?>][state]" class="form-control" placeholder="State/Province" value="<?php echo htmlspecialchars($address['state']); ?>" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="text" name="address[<?php echo $index; ?>][postal_code]" class="form-control" placeholder="Postal Code" value="<?php echo htmlspecialchars($address['zip_code']); ?>" required></div>
|
||||
</div>
|
||||
<div class="mb-3"><input type="text" name="address[<?php echo $index; ?>][country]" class="form-control" placeholder="Country" value="<?php echo htmlspecialchars($address['country']); ?>" required></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Uploads -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">File Uploads</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="fileUpload" class="form-label">Upload New File</label>
|
||||
<div class="input-group">
|
||||
<input type="file" class="form-control" id="fileUpload" name="file_upload">
|
||||
<button class="btn btn-outline-secondary" type="submit" name="upload_file">Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h5>Uploaded Files</h5>
|
||||
<ul class="list-group">
|
||||
<?php if (empty($files)): ?>
|
||||
<li class="list-group-item">No files uploaded yet.</li>
|
||||
<?php else: ?>
|
||||
<?php foreach ($files as $file): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<a href="uploads/<?php echo $file['filepath']; ?>" target="_blank"><?php echo htmlspecialchars($file['filename']); ?></a>
|
||||
<a href="delete_file.php?id=<?php echo $file['id']; ?>&customer_id=<?php echo $application_id; ?>" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this file?')">Delete</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" name="save_changes" class="btn btn-success">Save Changes</button>
|
||||
<a href="view_application.php?id=<?php echo $customer['id']; ?>" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
let contactIndex = <?php echo count($contacts); ?>;
|
||||
document.getElementById('addContact').addEventListener('click', function () {
|
||||
const container = document.getElementById('contactsContainer');
|
||||
const newContact = document.createElement('div');
|
||||
newContact.className = 'contact-group border p-3 mb-3';
|
||||
newContact.innerHTML = `
|
||||
<button type="button" class="btn-close float-end" aria-label="Close" onclick="if(confirm('Are you sure you want to delete this contact?')) { this.parentElement.remove(); }"></button>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="radio" name="contact[${contactIndex}][is_primary]" id="contact_${contactIndex}_is_primary" value="1">
|
||||
<label class="form-check-label" for="contact_${contactIndex}_is_primary">Primary Contact</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3"><input type="text" name="contact[${contactIndex}][name]" class="form-control" placeholder="Name" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="email" name="contact[${contactIndex}][email]" class="form-control" placeholder="Email" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="tel" name="contact[${contactIndex}][phone]" class="form-control" placeholder="Phone"></div>
|
||||
</div>`;
|
||||
container.appendChild(newContact);
|
||||
contactIndex++;
|
||||
updateRadioListeners();
|
||||
});
|
||||
|
||||
let addressIndex = <?php echo count($addresses); ?>;
|
||||
document.getElementById('addAddress').addEventListener('click', function () {
|
||||
const container = document.getElementById('addressesContainer');
|
||||
const newAddress = document.createElement('div');
|
||||
newAddress.className = 'address-group border p-3 mb-3';
|
||||
newAddress.innerHTML = `
|
||||
<button type="button" class="btn-close float-end" aria-label="Close" onclick="if(confirm('Are you sure you want to delete this address?')) { this.parentElement.remove(); }"></button>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<select name="address[${addressIndex}][type]" class="form-select" required>
|
||||
<option value="BILLING">Billing</option>
|
||||
<option value="SHIPPING">Shipping</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3"><input type="text" name="address[${addressIndex}][line1]" class="form-control" placeholder="Address Line 1" required></div>
|
||||
<div class="mb-3"><input type="text" name="address[${addressIndex}][line2]" class="form-control" placeholder="Address Line 2"></div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3"><input type="text" name="address[${addressIndex}][city]" class="form-control" placeholder="City" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="text" name="address[${addressIndex}][state]" class="form-control" placeholder="State/Province" required></div>
|
||||
<div class="col-md-4 mb-3"><input type="text" name="address[${addressIndex}][postal_code]" class="form-control" placeholder="Postal Code" required></div>
|
||||
</div>
|
||||
<div class="mb-3"><input type="text" name="address[${addressIndex}][country]" class="form-control" placeholder="Country" required></div>`;
|
||||
container.appendChild(newAddress);
|
||||
addressIndex++;
|
||||
});
|
||||
|
||||
function updateRadioListeners() {
|
||||
const radios = document.querySelectorAll('input[type="radio"][name^="contact"]');
|
||||
radios.forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
radios.forEach(r => {
|
||||
if (r !== this) r.checked = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateRadioListeners();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
34
includes/auth_helpers.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
function hasPermission($permission_name) {
|
||||
if (isset($_SESSION['user']['permissions']) && in_array($permission_name, $_SESSION['user']['permissions'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_user_role_id() {
|
||||
if (isset($_SESSION['user']['role_id'])) {
|
||||
return $_SESSION['user']['role_id'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function redirect_if_not_authenticated() {
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
function redirect_if_no_permission($permission_name) {
|
||||
if (!hasPermission($permission_name)) {
|
||||
// You can redirect to a specific error page or the index page with an error message.
|
||||
$_SESSION['flash_message'] = [
|
||||
'type' => 'danger',
|
||||
'message' => 'You do not have permission to access this page.'
|
||||
];
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
351
index.php
@ -1,150 +1,217 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
|
||||
// Protect route: check if user is logged in
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$user = $_SESSION['user'];
|
||||
|
||||
// Dynamic project data from environment
|
||||
$projectName = $_SERVER['PROJECT_NAME'] ?? 'Customer Master';
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Customer Master Registration & Maintenance';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
?>
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes bg-pan {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.hint {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Dashboard - <?php echo htmlspecialchars($projectName); ?></title>
|
||||
<meta name="description" content="<?php echo htmlspecialchars($projectDescription); ?>">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="<?php echo htmlspecialchars($projectName); ?>">
|
||||
<meta property="og:description" content="<?php echo htmlspecialchars($projectDescription); ?>">
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="og:image" content="<?php echo htmlspecialchars($projectImageUrl); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="<?php echo htmlspecialchars($projectName); ?>">
|
||||
<meta property="twitter:description" content="<?php echo htmlspecialchars($projectDescription); ?>">
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="twitter:image" content="<?php echo htmlspecialchars($projectImageUrl); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#"><i class="bi bi-person-vcard"></i> <?php echo htmlspecialchars($projectName); ?></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="mainNav">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#">Dashboard</a>
|
||||
</li>
|
||||
<?php if (hasPermission('manage_users')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="manage_users.php">Manage Users</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="view_applications.php">View Applications</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-person-circle"></i> <?php echo htmlspecialchars($user['username']); ?>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="profile.php">Profile</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="logout.php"><i class="bi bi-box-arrow-right"></i> Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container mt-4">
|
||||
<?php if (isset($_SESSION['flash_message'])): ?>
|
||||
<div class="alert alert-<?php echo $_SESSION['flash_message']['type']; ?> alert-dismissible fade show" role="alert">
|
||||
<?php echo $_SESSION['flash_message']['message']; ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['flash_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">Dashboard</h1>
|
||||
<?php if (hasPermission('create_application')): ?>
|
||||
<a href="new_application.php" class="btn btn-primary"><i class="bi bi-plus-circle"></i> New Customer Application</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<p>Your dashboard is ready. From here you can manage customer applications.</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0"><i class="bi bi-card-list"></i> Submitted Applications</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="index.php" method="GET" class="row g-3 align-items-center mb-3">
|
||||
<div class="col-auto">
|
||||
<label for="statusFilter" class="col-form-label">Filter by Status:</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select name="status" id="statusFilter" class="form-select">
|
||||
<option value="">All</option>
|
||||
<option value="draft" <?php echo (($_GET['status'] ?? '') === 'draft' ? 'selected' : ''); ?>>Draft</option>
|
||||
<option value="pending_approval" <?php echo (($_GET['status'] ?? '') === 'pending_approval' ? 'selected' : ''); ?>>Pending Approval</option>
|
||||
<option value="approved" <?php echo (($_GET['status'] ?? '') === 'approved' ? 'selected' : ''); ?>>Approved</option>
|
||||
<option value="rejected" <?php echo (($_GET['status'] ?? '') === 'rejected' ? 'selected' : ''); ?>>Rejected</option>
|
||||
<option value="reverted" <?php echo (($_GET['status'] ?? '') === 'reverted' ? 'selected' : ''); ?>>Reverted</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-info">Filter</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
function getSortIcon($column, $sort_column, $sort_order) {
|
||||
if ($column === $sort_column) {
|
||||
return $sort_order === 'ASC' ? ' <i class="bi bi-arrow-up"></i>' : ' <i class="bi bi-arrow-down"></i>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getStatusBadgeClass($status) {
|
||||
switch ($status) {
|
||||
case 'pending_approval':
|
||||
return 'bg-warning';
|
||||
case 'approved':
|
||||
return 'bg-success';
|
||||
case 'rejected':
|
||||
return 'bg-danger';
|
||||
case 'reverted':
|
||||
return 'bg-info';
|
||||
case 'draft':
|
||||
default:
|
||||
return 'bg-secondary';
|
||||
}
|
||||
}
|
||||
|
||||
$sort_column = $_GET['sort'] ?? 'created_at';
|
||||
$sort_order = $_GET['order'] ?? 'DESC';
|
||||
$valid_columns = ['id', 'application_id', 'company_name', 'status', 'created_at'];
|
||||
if (!in_array($sort_column, $valid_columns)) {
|
||||
$sort_column = 'created_at';
|
||||
}
|
||||
|
||||
$status_filter = $_GET['status'] ?? '';
|
||||
$sql = "SELECT id, application_id, company_name, status, created_at FROM customer_applications";
|
||||
$params = [];
|
||||
|
||||
if ($status_filter) {
|
||||
$sql .= " WHERE status = ?";
|
||||
$params[] = $status_filter;
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY $sort_column $sort_order";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$applications = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (count($applications) > 0) {
|
||||
echo '<table class="table table-striped table-hover">';
|
||||
echo '<thead>';
|
||||
echo '<tr>';
|
||||
echo '<th><a href="?sort=application_id&order=' . ($sort_column == 'application_id' && $sort_order == 'ASC' ? 'DESC' : 'ASC') . '&status=' . htmlspecialchars($status_filter) . '">Application ID</a>' . getSortIcon('application_id', $sort_column, $sort_order) . '</th>';
|
||||
echo '<th><a href="?sort=company_name&order=' . ($sort_column == 'company_name' && $sort_order == 'ASC' ? 'DESC' : 'ASC') . '&status=' . htmlspecialchars($status_filter) . '">Company Name</a>' . getSortIcon('company_name', $sort_column, $sort_order) . '</th>';
|
||||
echo '<th><a href="?sort=status&order=' . ($sort_column == 'status' && $sort_order == 'ASC' ? 'DESC' : 'ASC') . '&status=' . htmlspecialchars($status_filter) . '">Status</a>' . getSortIcon('status', $sort_column, $sort_order) . '</th>';
|
||||
echo '<th><a href="?sort=created_at&order=' . ($sort_column == 'created_at' && $sort_order == 'ASC' ? 'DESC' : 'ASC') . '&status=' . htmlspecialchars($status_filter) . '">Date Submitted</a>' . getSortIcon('created_at', $sort_column, $sort_order) . '</th>';
|
||||
echo '</tr>';
|
||||
echo '</thead>';
|
||||
echo '<tbody>';
|
||||
foreach ($applications as $app) { $badgeClass = getStatusBadgeClass($app['status']);
|
||||
echo '<tr>';
|
||||
echo '<td><a href="view_application.php?id=' . htmlspecialchars($app['id']) . '">' . htmlspecialchars($app['application_id']) . '</a></td>';
|
||||
echo '<td>' . htmlspecialchars($app['company_name']) . '</td>';
|
||||
echo '<td><span class="badge ' . $badgeClass . '">' . htmlspecialchars(ucfirst(str_replace('_', ' ', $app['status']))) . '</span></td>';
|
||||
echo '<td>' . htmlspecialchars(date("Y-m-d H:i", strtotime($app['created_at']))) . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
} else {
|
||||
echo '<p class="text-center">No customer applications found.</p>';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo '<div class="alert alert-danger">Error: Could not fetch applications.</div>';
|
||||
// Optional: log error to a file
|
||||
// error_log($e->getMessage());
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
65
login.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
session_start();
|
||||
// If user is already logged in, redirect to dashboard
|
||||
if (isset($_SESSION['user'])) {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Customer Master</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.login-card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card login-card shadow-sm">
|
||||
<div class="card-body p-5">
|
||||
<h1 class="card-title text-center mb-4">Customer Master</h1>
|
||||
<h5 class="card-subtitle mb-4 text-center text-muted">Please sign in</h5>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill"></i> <?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="auth.php" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required autofocus>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary">Sign in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center text-muted py-3">
|
||||
<small>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Flatlogic'); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
264
manage_users.php
Normal file
@ -0,0 +1,264 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
redirect_if_not_authenticated();
|
||||
redirect_if_no_permission('manage_users');
|
||||
|
||||
$pdo = db();
|
||||
$user = $_SESSION['user'];
|
||||
|
||||
// Handle POST requests for Create, Update, Delete
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
try {
|
||||
if ($action === 'create_user') {
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
$role_id = $_POST['role_id'];
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, password, role_id) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$username, $hashed_password, $role_id]);
|
||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'User created successfully.'];
|
||||
}
|
||||
|
||||
if ($action === 'update_role') {
|
||||
$user_id = $_POST['user_id'];
|
||||
$role_id = $_POST['role_id'];
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE users SET role_id = ? WHERE id = ?");
|
||||
$stmt->execute([$role_id, $user_id]);
|
||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'User role updated successfully.'];
|
||||
}
|
||||
|
||||
if ($action === 'delete_user') {
|
||||
$user_id = $_POST['user_id'];
|
||||
|
||||
// Prevent admin from deleting themselves
|
||||
if ($user_id == get_user_id()) {
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'You cannot delete your own account.'];
|
||||
} else {
|
||||
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'User deleted successfully.'];
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Database error: ' . $e->getMessage()];
|
||||
}
|
||||
|
||||
header('Location: manage_users.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch all users and roles
|
||||
$stmt_users = $pdo->query("SELECT u.id, u.username, r.name as role_name, u.created_at FROM users u JOIN roles r ON u.role_id = r.id ORDER BY u.created_at DESC");
|
||||
$users_list = $stmt_users->fetchAll();
|
||||
|
||||
$stmt_roles = $pdo->query("SELECT id, name FROM roles ORDER BY name");
|
||||
$roles = $stmt_roles->fetchAll();
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Manage Users</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="index.php"><i class="bi bi-person-vcard"></i> Customer Master</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php">Dashboard</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="manage_users.php">Manage Users</a></li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown"><i class="bi bi-person-circle"></i> <?= htmlspecialchars($user['username']) ?></a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container mt-4">
|
||||
<?php if (isset($_SESSION['flash_message'])): ?>
|
||||
<div class="alert alert-<?= $_SESSION['flash_message']['type'] ?> alert-dismissible fade show" role="alert">
|
||||
<?= $_SESSION['flash_message']['message'] ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['flash_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1 class="h3">Manage Users</h1>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createUserModal"><i class="bi bi-plus-circle"></i> Create User</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Role</th>
|
||||
<th>Created At</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users_list as $u): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($u['username']) ?></td>
|
||||
<td><?= htmlspecialchars($u['role_name']) ?></td>
|
||||
<td><?= date('Y-m-d H:i', strtotime($u['created_at'])) ?></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editRoleModal" data-user-id="<?= $u['id'] ?>" data-user-role="<?= $u['role_name'] ?>">
|
||||
<i class="bi bi-pencil-square"></i> Edit Role
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" data-bs-toggle="modal" data-bs-target="#deleteUserModal" data-user-id="<?= $u['id'] ?>">
|
||||
<i class="bi bi-trash"></i> Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Create User Modal -->
|
||||
<div class="modal fade" id="createUserModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form action="manage_users.php" method="POST">
|
||||
<input type="hidden" name="action" value="create_user">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Create New User</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="role_id" class="form-label">Role</label>
|
||||
<select class="form-select" id="role_id" name="role_id" required>
|
||||
<?php foreach ($roles as $role): ?>
|
||||
<option value="<?= $role['id'] ?>"><?= htmlspecialchars($role['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Create User</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Role Modal -->
|
||||
<div class="modal fade" id="editRoleModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form action="manage_users.php" method="POST">
|
||||
<input type="hidden" name="action" value="update_role">
|
||||
<input type="hidden" name="user_id" id="editUserId">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edit User Role</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="edit_role_id" class="form-label">Role</label>
|
||||
<select class="form-select" id="edit_role_id" name="role_id" required>
|
||||
<?php foreach ($roles as $role): ?>
|
||||
<option value="<?= $role['id'] ?>"><?= htmlspecialchars($role['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete User Modal -->
|
||||
<div class="modal fade" id="deleteUserModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form action="manage_users.php" method="POST">
|
||||
<input type="hidden" name="action" value="delete_user">
|
||||
<input type="hidden" name="user_id" id="deleteUserId">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Confirm Deletion</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete this user? This action cannot be undone.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger">Delete User</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Script to handle modal data population
|
||||
const editRoleModal = document.getElementById('editRoleModal');
|
||||
editRoleModal.addEventListener('show.bs.modal', event => {
|
||||
const button = event.relatedTarget;
|
||||
const userId = button.getAttribute('data-user-id');
|
||||
const userRole = button.getAttribute('data-user-role');
|
||||
|
||||
const modalUserIdInput = editRoleModal.querySelector('#editUserId');
|
||||
const modalRoleSelect = editRoleModal.querySelector('#edit_role_id');
|
||||
|
||||
modalUserIdInput.value = userId;
|
||||
|
||||
// Select the correct role in the dropdown
|
||||
for (let i = 0; i < modalRoleSelect.options.length; i++) {
|
||||
if (modalRoleSelect.options[i].text === userRole) {
|
||||
modalRoleSelect.selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const deleteUserModal = document.getElementById('deleteUserModal');
|
||||
deleteUserModal.addEventListener('show.bs.modal', event => {
|
||||
const button = event.relatedTarget;
|
||||
const userId = button.getAttribute('data-user-id');
|
||||
const modalUserIdInput = deleteUserModal.querySelector('#deleteUserId');
|
||||
modalUserIdInput.value = userId;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
461
new_application.php
Normal file
@ -0,0 +1,461 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
redirect_if_not_authenticated();
|
||||
redirect_if_no_permission('create_application');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>New Customer Application</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.form-step { display: none; }
|
||||
.form-step-active { display: block; }
|
||||
.card-header { font-weight: bold; }
|
||||
.form-section-title {
|
||||
font-weight: bold;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Customer Master</a>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php">Dashboard</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mt-4">
|
||||
<h2>New Customer Application</h2>
|
||||
|
||||
<?php if (isset($_SESSION['flash_message'])): ?>
|
||||
<div class="alert alert-<?php echo $_SESSION['flash_message']['type']; ?> alert-dismissible fade show" role="alert">
|
||||
<?php echo $_SESSION['flash_message']['message']; ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['flash_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="submit_application.php" method="POST" id="applicationForm">
|
||||
|
||||
<!-- Step 1: Customer Information -->
|
||||
<div id="step-1" class="form-step form-step-active">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">PAGE 1 of 6: CUSTOMER INFORMATION</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-3">
|
||||
<label for="company_name" class="form-label">Company Name</label>
|
||||
<input type="text" class="form-control" id="company_name" name="company_name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="company_address" class="form-label">Address</label>
|
||||
<textarea class="form-control" id="company_address" name="company_address" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="company_phone" class="form-label">Tel</label>
|
||||
<input type="tel" class="form-control" id="company_phone" name="company_phone">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="fax" class="form-label">Fax</label>
|
||||
<input type="tel" class="form-control" id="fax" name="fax">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="gst_reg_no" class="form-label">GST Reg. No.</label>
|
||||
<input type="text" class="form-control" id="gst_reg_no" name="gst_reg_no">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="company_reg_no" class="form-label">Company Reg. No.</label>
|
||||
<input type="text" class="form-control" id="company_reg_no" name="company_reg_no">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="date_of_incorporation" class="form-label">Date of Incorporation</label>
|
||||
<input type="date" class="form-control" id="date_of_incorporation" name="date_of_incorporation">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="country_of_incorporation" class="form-label">Country of Incorporation</label>
|
||||
<input type="text" class="form-control" id="country_of_incorporation" name="country_of_incorporation">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nature of Business</label>
|
||||
<div class="d-flex">
|
||||
<div class="form-check me-3"><input class="form-check-input" type="radio" name="nature_of_business" id="nature_manufacturing" value="Manufacturing"><label class="form-check-label" for="nature_manufacturing">Manufacturing</label></div>
|
||||
<div class="form-check me-3"><input class="form-check-input" type="radio" name="nature_of_business" id="nature_trading" value="Trading"><label class="form-check-label" for="nature_trading">Trading</label></div>
|
||||
<div class="form-check me-3"><input class="form-check-input" type="radio" name="nature_of_business" id="nature_service" value="Service"><label class="form-check-label" for="nature_service">Service</label></div>
|
||||
<div class="form-check"><input class="form-check-input" type="radio" name="nature_of_business" id="nature_others" value="Others"><label class="form-check-label" for="nature_others">Others</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="contact_person_name" class="form-label">Contact Person</label>
|
||||
<input type="text" class="form-control" id="contact_person_name" name="contact_person_name">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="contact_person_designation" class="form-label">Designation</label>
|
||||
<input type="text" class="form-control" id="contact_person_designation" name="contact_person_designation">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="contact_person_phone" class="form-label">Contact No</label>
|
||||
<input type="text" class="form-control" id="contact_person_phone" name="contact_person_phone">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="contact_person_email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="contact_person_email" name="contact_person_email">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="credit_terms_requested" class="form-label">Credit Terms Requested</label>
|
||||
<input type="text" class="form-control" id="credit_terms_requested" name="credit_terms_requested">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="credit_limit_requested" class="form-label">Credit Limit Requested</label>
|
||||
<input type="text" class="form-control" id="credit_limit_requested" name="credit_limit_requested">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section-title">ACCOUNT SET UP FOR</div>
|
||||
<div class="d-flex">
|
||||
<div class="form-check me-3"><input class="form-check-input" type="checkbox" name="account_setup_ar_statement" id="account_setup_ar_statement" value="1"><label class="form-check-label" for="account_setup_ar_statement">A/R Statement</label></div>
|
||||
<div class="form-check me-3"><input class="form-check-input" type="checkbox" name="account_setup_dunning_letter" id="account_setup_dunning_letter" value="1"><label class="form-check-label" for="account_setup_dunning_letter">Dunning Letter</label></div>
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" name="account_setup_ap_payment" id="account_setup_ap_payment" value="1"><label class="form-check-label" for="account_setup_ap_payment">A/P-Payment</label></div>
|
||||
</div>
|
||||
|
||||
<div class="form-section-title">DELIVERY TO</div>
|
||||
<div class="mb-3">
|
||||
<label for="del_to_address" class="form-label">Address</label>
|
||||
<textarea class="form-control" id="del_to_address" name="del_to_address" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="del_to_tel" class="form-label">Tel</label>
|
||||
<input type="tel" class="form-control" id="del_to_tel" name="del_to_tel">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="del_to_fax" class="form-label">Fax</label>
|
||||
<input type="tel" class="form-control" id="del_to_fax" name="del_to_fax">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="del_to_contact_person" class="form-label">Contact Person</label>
|
||||
<input type="text" class="form-control" id="del_to_contact_person" name="del_to_contact_person">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="del_to_designation" class="form-label">Designation</label>
|
||||
<input type="text" class="form-control" id="del_to_designation" name="del_to_designation">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="del_to_email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="del_to_email" name="del_to_email">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="button" class="btn btn-primary next-step">Next <i class="bi bi-arrow-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Shareholder/Director Information -->
|
||||
<div id="step-2" class="form-step">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>PAGE 2 of 6: PARTICULARS OF SHAREHOLDER'S / DIRECTOR'S</span>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="addShareholder"><i class="bi bi-plus-circle"></i> Add More</button>
|
||||
</div>
|
||||
<div class="card-body" id="shareholdersContainer">
|
||||
<!-- Dynamic content will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-secondary prev-step"><i class="bi bi-arrow-left"></i> Previous</button>
|
||||
<button type="button" class="btn btn-primary next-step">Next <i class="bi bi-arrow-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Trade References -->
|
||||
<div id="step-3" class="form-step">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>PAGE 3 of 6: TRADE REFERENCES</span>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="addTradeReference"><i class="bi bi-plus-circle"></i> Add More</button>
|
||||
</div>
|
||||
<div class="card-body" id="tradeReferencesContainer">
|
||||
<!-- Dynamic content will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-secondary prev-step"><i class="bi bi-arrow-left"></i> Previous</button>
|
||||
<button type="button" class="btn btn-primary next-step">Next <i class="bi bi-arrow-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Banker's Information -->
|
||||
<div id="step-4" class="form-step">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">PAGE 4 of 6: BANKER'S INFORMATION</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="bank_name" class="form-label">Bank Name</label>
|
||||
<input type="text" class="form-control" id="bank_name" name="bank_name">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="bank_address" class="form-label">Address</label>
|
||||
<textarea class="form-control" id="bank_address" name="bank_address" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="bank_swift_code" class="form-label">Swift Code</label>
|
||||
<input type="text" class="form-control" id="bank_swift_code" name="bank_swift_code">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="bank_account_no" class="form-label">Account No</label>
|
||||
<input type="text" class="form-control" id="bank_account_no" name="bank_account_no">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="bank_contact_person" class="form-label">Contact Person</label>
|
||||
<input type="text" class="form-control" id="bank_contact_person" name="bank_contact_person">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="bank_telephone_no" class="form-label">Telephone No</label>
|
||||
<input type="tel" class="form-control" id="bank_telephone_no" name="bank_telephone_no">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="bank_fax_no" class="form-label">Fax No</label>
|
||||
<input type="tel" class="form-control" id="bank_fax_no" name="bank_fax_no">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-secondary prev-step"><i class="bi bi-arrow-left"></i> Previous</button>
|
||||
<button type="button" class="btn btn-primary next-step">Next <i class="bi bi-arrow-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 5: Financial Information -->
|
||||
<div id="step-5" class="form-step">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">PAGE 5 of 6: FINANCIAL / CREDIT INFORMATION</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="paid_up_capital" class="form-label">Paid-up Capital</label>
|
||||
<input type="number" class="form-control" id="paid_up_capital" name="paid_up_capital" step="0.01">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="latest_audited_financial_year" class="form-label">For Latest Audited Financial Year</label>
|
||||
<input type="text" class="form-control" id="latest_audited_financial_year" name="latest_audited_financial_year" placeholder="e.g., 2023">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="currency" class="form-label">Currency</label>
|
||||
<input type="text" class="form-control" id="currency" name="currency" placeholder="e.g., SGD">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="shareholder_equity" class="form-label">Shareholder's Equity</label>
|
||||
<input type="number" class="form-control" id="shareholder_equity" name="shareholder_equity" step="0.01">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="annual_turnover" class="form-label">Annual Turnover</label>
|
||||
<input type="number" class="form-control" id="annual_turnover" name="annual_turnover" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="net_profit_loss" class="form-label">Net Profit / (Loss)</label>
|
||||
<input type="number" class="form-control" id="net_profit_loss" name="net_profit_loss" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-secondary prev-step"><i class="bi bi-arrow-left"></i> Previous</button>
|
||||
<button type="button" class="btn btn-primary next-step">Next <i class="bi bi-arrow-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 6: Declaration -->
|
||||
<div id="step-6" class="form-step">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">PAGE 6 of 6: DECLARATION AND AUTHORISATION</div>
|
||||
<div class="card-body">
|
||||
<p class="small">We, the undersigned, hereby apply for credit facility with your company and we declare that the information given above and in any attached documents is true and correct. We authorise you or your representative to obtain any further information you may require from our banker(s) or other sources to determine our credit worthiness and we agree to abide by your standard terms and conditions of sales.</p>
|
||||
<p class="small">We futher undertake to inform you in writing of any changes in our shareholding / directorship / management and registered particulars, and we agree that we are liable for all debts incurred until such notice is received by you.</p>
|
||||
<p class="small">In the event that the credit application is not approved, we understand that you are not obliged to assign any reason for the decision.</p>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="declaration_name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="declaration_name" name="declaration_name">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="declaration_designation" class="form-label">Designation</label>
|
||||
<input type="text" class="form-control" id="declaration_designation" name="declaration_designation">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="declaration_signature" class="form-label">Signature / Company Stamp</label>
|
||||
<div class="border p-2" style="height: 100px;">
|
||||
<!-- Placeholder for signature pad or image upload -->
|
||||
<small class="text-muted">Signature area</small>
|
||||
</div>
|
||||
<input type="hidden" name="declaration_signature_data" id="declaration_signature_data">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="declaration_date" class="form-label">Date</label>
|
||||
<input type="date" class="form-control" id="declaration_date" name="declaration_date">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-secondary prev-step"><i class="bi bi-arrow-left"></i> Previous</button>
|
||||
<button type="submit" class="btn btn-success"><i class="bi bi-check-circle"></i> Submit Application</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
let currentStep = 1;
|
||||
const steps = document.querySelectorAll('.form-step');
|
||||
|
||||
function showStep(step) {
|
||||
steps.forEach((el, index) => {
|
||||
el.classList.toggle('form-step-active', index + 1 === step);
|
||||
});
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
document.querySelectorAll('.next-step').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
// Add validation here if needed
|
||||
if (currentStep < steps.length) {
|
||||
currentStep++;
|
||||
showStep(currentStep);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.prev-step').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
if (currentStep > 1) {
|
||||
currentStep--;
|
||||
showStep(currentStep);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// --- Dynamic Rows ---
|
||||
let shareholderIndex = 0;
|
||||
const addShareholderBtn = document.getElementById('addShareholder');
|
||||
const shareholdersContainer = document.getElementById('shareholdersContainer');
|
||||
|
||||
function addShareholderRow() {
|
||||
const index = shareholderIndex++;
|
||||
const newRow = document.createElement('div');
|
||||
newRow.className = 'shareholder-group border rounded p-3 mb-3 position-relative';
|
||||
newRow.innerHTML = `
|
||||
<button type="button" class="btn-close position-absolute top-0 end-0 p-2" aria-label="Close" onclick="this.parentElement.remove()"></button>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-2">
|
||||
<label class="form-label">Name</label>
|
||||
<input type="text" name="shareholder[${index}][name]" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-6 mb-2">
|
||||
<label class="form-label">NRIC/FIN No.</label>
|
||||
<input type="text" name="shareholder[${index}][nric_fin]" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Address</label>
|
||||
<input type="text" name="shareholder[${index}][address]" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-2">
|
||||
<label class="form-label">% of Shareholding</label>
|
||||
<input type="number" name="shareholder[${index}][perc_of_shareholding]" class="form-control form-control-sm" step="0.01">
|
||||
</div>
|
||||
<div class="col-md-6 mb-2">
|
||||
<label class="form-label">Contact No.</label>
|
||||
<input type="tel" name="shareholder[${index}][contact_no]" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>`;
|
||||
shareholdersContainer.appendChild(newRow);
|
||||
}
|
||||
|
||||
if(addShareholderBtn) addShareholderBtn.addEventListener('click', addShareholderRow);
|
||||
|
||||
|
||||
let tradeReferenceIndex = 0;
|
||||
const addTradeRefBtn = document.getElementById('addTradeReference');
|
||||
const tradeRefsContainer = document.getElementById('tradeReferencesContainer');
|
||||
|
||||
function addTradeReferenceRow() {
|
||||
const index = tradeReferenceIndex++;
|
||||
const newRow = document.createElement('div');
|
||||
newRow.className = 'trade-reference-group border rounded p-3 mb-3 position-relative';
|
||||
newRow.innerHTML = `
|
||||
<button type="button" class="btn-close position-absolute top-0 end-0 p-2" aria-label="Close" onclick="this.parentElement.remove()"></button>
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-2">
|
||||
<label class="form-label">Company Name</label>
|
||||
<input type="text" name="trade_reference[${index}][company_name]" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-2">
|
||||
<label class="form-label">Contact Person</label>
|
||||
<input type="text" name="trade_reference[${index}][contact_person]" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-4 mb-2">
|
||||
<label class="form-label">Telephone No.</label>
|
||||
<input type="tel" name="trade_reference[${index}][telephone_no]" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-4 mb-2">
|
||||
<label class="form-label">Fax No.</label>
|
||||
<input type="tel" name="trade_reference[${index}][fax_no]" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>`;
|
||||
tradeRefsContainer.appendChild(newRow);
|
||||
}
|
||||
|
||||
if(addTradeRefBtn) addTradeRefBtn.addEventListener('click', addTradeReferenceRow);
|
||||
|
||||
// Initialize first rows
|
||||
addShareholderRow();
|
||||
addTradeReferenceRow();
|
||||
addTradeReferenceRow();
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
7
permissions.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
function has_permission($permission) {
|
||||
if (isset($_SESSION['user']['permissions']) && in_array($permission, $_SESSION['user']['permissions'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
172
process_approval.php
Normal file
@ -0,0 +1,172 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
require_once 'db/config.php';
|
||||
require_once 'mail/MailService.php';
|
||||
|
||||
redirect_if_not_authenticated();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
exit('Method Not Allowed');
|
||||
}
|
||||
|
||||
$application_id = $_POST['application_id'] ?? null;
|
||||
$action = $_POST['action'] ?? null;
|
||||
$comments = $_POST['comments'] ?? '';
|
||||
|
||||
if (!$application_id || !$action) {
|
||||
header('Location: view_applications.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// --- New Approval Workflow ---
|
||||
$approval_levels = [
|
||||
1 => 'Sales Manager',
|
||||
2 => 'General Manager',
|
||||
3 => 'Managing Director'
|
||||
];
|
||||
// --- End New Approval Workflow ---
|
||||
|
||||
// Helper function to get user emails by role name
|
||||
function get_user_emails_by_role($role_name, $pdo) {
|
||||
$stmt = $pdo->prepare('SELECT email FROM users u JOIN roles r ON u.role_id = r.id WHERE r.name = ?');
|
||||
$stmt->execute([$role_name]);
|
||||
return $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
}
|
||||
|
||||
// Fetch application
|
||||
$stmt = $pdo->prepare('SELECT * FROM customer_applications WHERE id = ?');
|
||||
$stmt->execute([$application_id]);
|
||||
$application = $stmt->fetch();
|
||||
|
||||
if (!$application) {
|
||||
die('Application not found.');
|
||||
}
|
||||
|
||||
// Get applicant email
|
||||
$stmt_applicant = $pdo->prepare('SELECT email FROM customer_contacts WHERE customer_application_id = ? AND is_primary = 1');
|
||||
$stmt_applicant->execute([$application_id]);
|
||||
$applicant_email = $stmt_applicant->fetchColumn();
|
||||
|
||||
// Get current user's role
|
||||
$current_user_role_id = get_user_role_id();
|
||||
$stmt_role = $pdo->prepare("SELECT name FROM roles WHERE id = ?");
|
||||
$stmt_role->execute([$current_user_role_id]);
|
||||
$current_user_role_name = $stmt_role->fetchColumn();
|
||||
|
||||
$current_level = $application['approval_level'];
|
||||
$required_role = $approval_levels[$current_level] ?? null;
|
||||
|
||||
// Check if the current user has the required role for this level
|
||||
if ($current_user_role_name !== $required_role && $current_user_role_name !== 'admin') {
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'You do not have permission to perform this action.'];
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Log the action
|
||||
$stmt_log = $pdo->prepare("INSERT INTO application_approvals (application_id, approver_id, approval_level, status, comments) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt_log->execute([$application_id, get_user_id(), $current_level, ucfirst($action), $comments]);
|
||||
|
||||
if ($action === 'approve') {
|
||||
$next_level = $current_level + 1;
|
||||
|
||||
if (array_key_exists($next_level, $approval_levels)) {
|
||||
// Move to next approval level
|
||||
$next_role_name = $approval_levels[$next_level];
|
||||
$stmt_next_role = $pdo->prepare("SELECT id FROM roles WHERE name = ?");
|
||||
$stmt_next_role->execute([$next_role_name]);
|
||||
$next_role_id = $stmt_next_role->fetchColumn();
|
||||
|
||||
$stmt_update = $pdo->prepare('UPDATE customer_applications SET approval_level = ?, current_approver_role_id = ?, status = \'Pending\' WHERE id = ?');
|
||||
$stmt_update->execute([$next_level, $next_role_id, $application_id]);
|
||||
|
||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => "Application approved and sent to {$next_role_name}."];
|
||||
|
||||
/*
|
||||
// Notify next approvers
|
||||
$next_approver_emails = get_user_emails_by_role($next_role_name, $pdo);
|
||||
if (!empty($next_approver_emails)) {
|
||||
$subject = "Application requires your approval: " . $application['company_name'];
|
||||
$body = "<p>A credit application for <b>{$application['company_name']}</b> requires your review.</p><p><a href='http://{\$_SERVER['HTTP_HOST']}/view_application.php?id={$application_id}'>View Application</a></p>";
|
||||
MailService::sendMail($next_approver_emails, $subject, $body);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
// Final approval
|
||||
$stmt_update = $pdo->prepare("UPDATE customer_applications SET status = 'Approved', approval_level = NULL, current_approver_role_id = NULL WHERE id = ?");
|
||||
$stmt_update->execute([$application_id]);
|
||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'Application has been fully approved.'];
|
||||
|
||||
/*
|
||||
// Notify applicant
|
||||
if ($applicant_email) {
|
||||
$subject = 'Your Application has been Approved: ' . $application['application_id'];
|
||||
$body = "<p>Congratulations! Your customer application ({$application['application_id']}) has been approved.</p>";
|
||||
MailService::sendMail($applicant_email, $subject, $body);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
} elseif ($action === 'return') {
|
||||
$prev_level = $current_level - 1;
|
||||
|
||||
if (array_key_exists($prev_level, $approval_levels)) {
|
||||
// Return to previous approval level
|
||||
$prev_role_name = $approval_levels[$prev_level];
|
||||
$stmt_prev_role = $pdo->prepare("SELECT id FROM roles WHERE name = ?");
|
||||
$stmt_prev_role->execute([$prev_role_name]);
|
||||
$prev_role_id = $stmt_prev_role->fetchColumn();
|
||||
|
||||
$stmt_update = $pdo->prepare('UPDATE customer_applications SET approval_level = ?, current_approver_role_id = ?, status = \'Returned\' WHERE id = ?');
|
||||
$stmt_update->execute([$prev_level, $prev_role_id, $application_id]);
|
||||
|
||||
$_SESSION['flash_message'] = ['type' => 'warning', 'message' => "Application returned to {$prev_role_name} for review."];
|
||||
|
||||
/*
|
||||
// Notify previous approvers
|
||||
$prev_approver_emails = get_user_emails_by_role($prev_role_name, $pdo);
|
||||
if (!empty($prev_approver_emails)) {
|
||||
$subject = "Application returned for your review: " . $application['company_name'];
|
||||
$body = "<p>The application for <b>{$application['company_name']}</b> has been returned for your review with the following comments:</p><p><i>" . htmlspecialchars($comments) . "</i></p><p><a href='http://{\$_SERVER['HTTP_HOST']}/view_application.php?id={$application_id}'>View Application</a></p>";
|
||||
MailService::sendMail($prev_approver_emails, $subject, $body);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
// Cannot return from the first level, this case should ideally be handled in the UI
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => "Cannot return the application from the first approval level."];
|
||||
}
|
||||
|
||||
} elseif ($action === 'reject') {
|
||||
$stmt_update = $pdo->prepare("UPDATE customer_applications SET status = 'Rejected' WHERE id = ?");
|
||||
$stmt_update->execute([$application_id]);
|
||||
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Application has been rejected.'];
|
||||
|
||||
/*
|
||||
// Notify applicant
|
||||
if ($applicant_email) {
|
||||
$subject = 'Your Application has been Rejected: ' . $application['application_id'];
|
||||
$body = "<p>We regret to inform you that your customer application ({$application['application_id']}) has been rejected. The following comments were provided:</p><p><i>" . htmlspecialchars($comments) . "</i></p>";
|
||||
MailService::sendMail($applicant_email, $subject, $body);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
error_log('Approval processing failed: ' . $e->getMessage());
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'An error occurred while processing the application. Please try again.'];
|
||||
}
|
||||
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
127
profile.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
|
||||
// Protect route: check if user is logged in
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$user = $_SESSION['user'];
|
||||
|
||||
// Dynamic project data from environment
|
||||
$projectName = $_SERVER['PROJECT_NAME'] ?? 'Customer Master';
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Customer Master Registration & Maintenance';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>User Profile - <?php echo htmlspecialchars($projectName); ?></title>
|
||||
<meta name="description" content="<?php echo htmlspecialchars($projectDescription); ?>">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="<?php echo htmlspecialchars($projectName); ?>">
|
||||
<meta property="og:description" content="<?php echo htmlspecialchars($projectDescription); ?>">
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="og:image" content="<?php echo htmlspecialchars($projectImageUrl); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="<?php echo htmlspecialchars($projectName); ?>">
|
||||
<meta property="twitter:description" content="<?php echo htmlspecialchars($projectDescription); ?>">
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="twitter:image" content="<?php echo htmlspecialchars($projectImageUrl); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="index.php"><i class="bi bi-person-vcard"></i> <?php echo htmlspecialchars($projectName); ?></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="mainNav">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">Dashboard</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle active" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-person-circle"></i> <?php echo htmlspecialchars($user['username']); ?>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="profile.php">Profile</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="logout.php"><i class="bi bi-box-arrow-right"></i> Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title"><i class="bi bi-person-badge"></i> User Profile</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Username:</strong> <?php echo htmlspecialchars($user['username']); ?></p>
|
||||
<p><strong>Role:</strong> <?php echo htmlspecialchars(ucfirst($user['role'])); ?></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h5><i class="bi bi-key"></i> Change Password</h5>
|
||||
|
||||
<?php if (isset($_SESSION['flash_message'])): ?>
|
||||
<div class="alert alert-<?php echo $_SESSION['flash_message']['type']; ?> alert-dismissible fade show" role="alert">
|
||||
<?php echo $_SESSION['flash_message']['message']; ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['flash_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="update_profile.php" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="current_password" class="form-label">Current Password</label>
|
||||
<input type="password" class="form-control" id="current_password" name="current_password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="new_password" class="form-label">New Password</label>
|
||||
<input type="password" class="form-control" id="new_password" name="new_password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirm_new_password" class="form-label">Confirm New Password</label>
|
||||
<input type="password" class="form-control" id="confirm_new_password" name="confirm_new_password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Update Password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
180
submit_application.php
Normal file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
require_once 'includes/auth_helpers.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
exit('Method Not Allowed');
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['user'])) {
|
||||
http_response_code(403);
|
||||
exit('Unauthorized');
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 1. Insert into customer_applications
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO customer_applications (
|
||||
application_id, customer_id, created_by, company_name, company_phone, fax, gst_reg_no,
|
||||
company_reg_no, date_of_incorporation, country_of_incorporation, nature_of_business,
|
||||
credit_terms_requested, credit_limit_requested, account_setup_ar_statement,
|
||||
account_setup_dunning_letter, account_setup_ap_payment,
|
||||
declaration_name, declaration_designation, declaration_date
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
$stmt->execute([
|
||||
uniqid(),
|
||||
$_SESSION['user']['id'],
|
||||
$_SESSION['user']['id'],
|
||||
$_POST['company_name'] ?? null,
|
||||
$_POST['company_phone'] ?? null,
|
||||
$_POST['fax'] ?? null,
|
||||
$_POST['gst_reg_no'] ?? null,
|
||||
$_POST['company_reg_no'] ?? null,
|
||||
$_POST['date_of_incorporation'] ?? null,
|
||||
$_POST['country_of_incorporation'] ?? null,
|
||||
$_POST['nature_of_business'] ?? null,
|
||||
$_POST['credit_terms_requested'] ?? null,
|
||||
$_POST['credit_limit_requested'] ?? null,
|
||||
isset($_POST['account_setup_ar_statement']) ? 1 : 0,
|
||||
isset($_POST['account_setup_dunning_letter']) ? 1 : 0,
|
||||
isset($_POST['account_setup_ap_payment']) ? 1 : 0,
|
||||
$_POST['declaration_name'] ?? null,
|
||||
$_POST['declaration_designation'] ?? null,
|
||||
$_POST['declaration_date'] ?? null
|
||||
]);
|
||||
$customer_application_id = $pdo->lastInsertId();
|
||||
|
||||
// Insert Primary Contact
|
||||
if (!empty($_POST['contact_person_name']) && !empty($_POST['contact_person_email'])) {
|
||||
$stmt_contact = $pdo->prepare(
|
||||
'INSERT INTO customer_contacts (customer_application_id, name, email, phone, is_primary) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
$stmt_contact->execute([
|
||||
$customer_application_id,
|
||||
$_POST['contact_person_name'],
|
||||
$_POST['contact_person_email'],
|
||||
$_POST['contact_person_phone'] ?? null,
|
||||
1 // Set as primary contact
|
||||
]);
|
||||
}
|
||||
|
||||
// Insert Billing Address
|
||||
if (!empty($_POST['company_address'])) {
|
||||
$stmt_address = $pdo->prepare(
|
||||
'INSERT INTO customer_addresses (customer_application_id, address_type, address_line_1) VALUES (?, ?, ?)'
|
||||
);
|
||||
$stmt_address->execute([$customer_application_id, 'BILLING', $_POST['company_address']]);
|
||||
}
|
||||
|
||||
// Insert Delivery Address
|
||||
if (!empty($_POST['del_to_address'])) {
|
||||
$stmt_del_address = $pdo->prepare(
|
||||
'INSERT INTO customer_addresses (customer_application_id, address_type, address_line_1) VALUES (?, ?, ?)'
|
||||
);
|
||||
$stmt_del_address->execute([$customer_application_id, 'SHIPPING', $_POST['del_to_address']]);
|
||||
}
|
||||
|
||||
// 2. Insert into shareholder_director_information
|
||||
if (isset($_POST['shareholder']) && is_array($_POST['shareholder'])) {
|
||||
$stmt_shareholder = $pdo->prepare(
|
||||
'INSERT INTO shareholder_director_information (application_id, name, address, nric_fin, perc_of_shareholding, contact_no) VALUES (?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
foreach ($_POST['shareholder'] as $shareholder) {
|
||||
if (empty($shareholder['name'])) continue; // Skip empty rows
|
||||
$stmt_shareholder->execute([
|
||||
$customer_application_id,
|
||||
$shareholder['name'] ?? null,
|
||||
$shareholder['address'] ?? null,
|
||||
$shareholder['nric_fin'] ?? null,
|
||||
$shareholder['perc_of_shareholding'] ?? null,
|
||||
$shareholder['contact_no'] ?? null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Insert into customer_trade_references
|
||||
if (isset($_POST['trade_reference']) && is_array($_POST['trade_reference'])) {
|
||||
$stmt_trade_ref = $pdo->prepare(
|
||||
'INSERT INTO customer_trade_references (customer_application_id, company_name, contact_person, telephone_no, fax_no) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
foreach ($_POST['trade_reference'] as $trade_ref) {
|
||||
if (empty($trade_ref['company_name'])) continue; // Skip empty rows
|
||||
$stmt_trade_ref->execute([
|
||||
$customer_application_id,
|
||||
$trade_ref['company_name'] ?? null,
|
||||
$trade_ref['contact_person'] ?? null,
|
||||
$trade_ref['telephone_no'] ?? null,
|
||||
$trade_ref['fax_no'] ?? null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Insert into customer_bank_details
|
||||
if (!empty($_POST['bank_name'])) {
|
||||
$stmt_bank = $pdo->prepare(
|
||||
'INSERT INTO customer_bank_details (customer_application_id, bank_name, address, swift_code, account_number, contact_person, telephone_no, fax_no) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
$stmt_bank->execute([
|
||||
$customer_application_id,
|
||||
$_POST['bank_name'] ?? null,
|
||||
$_POST['bank_address'] ?? null,
|
||||
$_POST['bank_swift_code'] ?? null,
|
||||
$_POST['bank_account_no'] ?? null,
|
||||
$_POST['bank_contact_person'] ?? null,
|
||||
$_POST['bank_telephone_no'] ?? null,
|
||||
$_POST['bank_fax_no'] ?? null
|
||||
]);
|
||||
}
|
||||
|
||||
// 5. Insert into customer_financial_information
|
||||
if (!empty($_POST['paid_up_capital'])) {
|
||||
$stmt_financial = $pdo->prepare(
|
||||
'INSERT INTO customer_financial_information (customer_application_id, latest_audited_financial_year, shareholder_equity, paid_up_capital, annual_turnover, net_profit_loss, currency) VALUES (?, ?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
$stmt_financial->execute([
|
||||
$customer_application_id,
|
||||
$_POST['latest_audited_financial_year'] ?? null,
|
||||
$_POST['shareholder_equity'] ?? null,
|
||||
$_POST['paid_up_capital'] ?? null,
|
||||
$_POST['annual_turnover'] ?? null,
|
||||
$_POST['net_profit_loss'] ?? null,
|
||||
$_POST['currency'] ?? null
|
||||
]);
|
||||
}
|
||||
|
||||
// Set initial approval status
|
||||
$stmt_role = $pdo->prepare("SELECT id FROM roles WHERE name = 'Sales Manager'");
|
||||
$stmt_role->execute();
|
||||
$approver_role = $stmt_role->fetch(PDO::FETCH_ASSOC);
|
||||
$approver_role_id = $approver_role ? $approver_role['id'] : null;
|
||||
|
||||
$stmt_update = $pdo->prepare('UPDATE customer_applications SET approval_level = 1, current_approver_role_id = ? WHERE id = ?');
|
||||
$stmt_update->execute([$approver_role_id, $customer_application_id]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
$_SESSION['flash_message'] = [
|
||||
'type' => 'success',
|
||||
'message' => 'Customer application submitted successfully!'
|
||||
];
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
error_log('Application submission failed: ' . $e->getMessage());
|
||||
|
||||
$_SESSION['flash_message'] = [
|
||||
'type' => 'danger',
|
||||
'message' => 'There was an error submitting your application. Please check the data and try again. Error: ' . $e->getMessage()
|
||||
];
|
||||
header('Location: new_application.php');
|
||||
exit();
|
||||
}
|
||||
69
submit_for_approval.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
redirect_if_not_authenticated();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['application_id'])) {
|
||||
header('Location: view_applications.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$application_id = $_POST['application_id'];
|
||||
$pdo = db();
|
||||
|
||||
// Get the role ID for the first approval level (Sales Manager)
|
||||
$stmt_role = $pdo->prepare("SELECT id FROM roles WHERE name = 'Sales Manager'");
|
||||
$stmt_role->execute();
|
||||
$sales_manager_role_id = $stmt_role->fetchColumn();
|
||||
|
||||
if (!$sales_manager_role_id) {
|
||||
$_SESSION['flash_message'] = [
|
||||
'type' => 'danger',
|
||||
'message' => 'Error: Sales Manager role not found. Cannot submit for approval.'
|
||||
];
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Update the application to start the approval process
|
||||
$stmt = $pdo->prepare(
|
||||
'UPDATE customer_applications
|
||||
SET status = ?, approval_level = ?, current_approver_role_id = ?
|
||||
WHERE id = ? AND status = ?'
|
||||
);
|
||||
|
||||
$success = $stmt->execute([
|
||||
'Pending',
|
||||
1, // Start at level 1
|
||||
$sales_manager_role_id,
|
||||
$application_id,
|
||||
'Draft' // Ensure we only update drafts
|
||||
]);
|
||||
|
||||
if ($success && $stmt->rowCount() > 0) {
|
||||
// Create initial approval history entry
|
||||
$stmt_history = $pdo->prepare(
|
||||
'INSERT INTO application_approvals (application_id, approver_id, status, comments, created_at) VALUES (?, ?, ?, ?, NOW())'
|
||||
);
|
||||
$stmt_history->execute([
|
||||
$application_id,
|
||||
get_user_id(), // The user submitting the application
|
||||
'Submitted',
|
||||
'Application submitted for approval.'
|
||||
]);
|
||||
|
||||
$_SESSION['flash_message'] = [
|
||||
'type' => 'success',
|
||||
'message' => 'Application successfully submitted for approval.'
|
||||
];
|
||||
} else {
|
||||
$_SESSION['flash_message'] = [
|
||||
'type' => 'danger',
|
||||
'message' => 'Failed to submit application. It might not be in a draft state or another error occurred.'
|
||||
];
|
||||
}
|
||||
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
179
update_application.php
Normal file
@ -0,0 +1,179 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
redirect_if_not_authenticated();
|
||||
redirect_if_no_permission('edit_application');
|
||||
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$customer_id = $_POST['customer_id'] ?? null;
|
||||
if (!$customer_id) {
|
||||
die("Invalid request.");
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Handle File Upload
|
||||
if (isset($_POST['upload_file'])) {
|
||||
if (isset($_FILES['file_upload']) && $_FILES['file_upload']['error'] === UPLOAD_ERR_OK) {
|
||||
$file_tmp_path = $_FILES['file_upload']['tmp_name'];
|
||||
$file_name = basename($_FILES['file_upload']['name']);
|
||||
|
||||
// Sanitize filename
|
||||
$new_file_name = preg_replace('/[^a-zA-Z0-9-_. ]/', '', $file_name);
|
||||
$unique_file_name = time() . '-' . $new_file_name;
|
||||
$dest_path = 'uploads/' . $unique_file_name;
|
||||
|
||||
if (move_uploaded_file($file_tmp_path, $dest_path)) {
|
||||
try {
|
||||
$sql = "INSERT INTO application_files (customer_application_id, filename, filepath) VALUES (?, ?, ?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$customer_id, $file_name, $unique_file_name]);
|
||||
$_SESSION['success_message'] = "File uploaded successfully.";
|
||||
} catch (PDOException $e) {
|
||||
$_SESSION['error_message'] = "Error saving file info: " . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Error moving uploaded file.";
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Error uploading file: " . $_FILES['file_upload']['error'];
|
||||
}
|
||||
header("Location: edit_application.php?id=" . $customer_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Handle Form Submission
|
||||
if (isset($_POST['save_changes'])) {
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 1. Update Customer Table
|
||||
$sql = "UPDATE customer_applications SET
|
||||
company_name = ?,
|
||||
company_website = ?,
|
||||
company_phone = ?,
|
||||
sales_owner = ?,
|
||||
payment_terms = ?,
|
||||
tags = ?,
|
||||
notes = ?
|
||||
WHERE id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([
|
||||
$_POST['company_name'],
|
||||
$_POST['company_website'],
|
||||
$_POST['company_phone'],
|
||||
$_POST['sales_owner'],
|
||||
$_POST['payment_terms'],
|
||||
$_POST['tags'],
|
||||
$_POST['notes'],
|
||||
$customer_id
|
||||
]);
|
||||
|
||||
// 2. Process Contacts
|
||||
$submitted_contact_ids = [];
|
||||
if (isset($_POST['contact']) && is_array($_POST['contact'])) {
|
||||
// Reset primary contact
|
||||
$reset_primary_stmt = $pdo->prepare("UPDATE customer_contacts SET is_primary = 0 WHERE customer_application_id = ?");
|
||||
$reset_primary_stmt->execute([$customer_id]);
|
||||
|
||||
foreach ($_POST['contact'] as $index => $contact_data) {
|
||||
$contact_id = $contact_data['id'] ?? null;
|
||||
$is_primary = (isset($contact_data['is_primary']) && $contact_data['is_primary'] == '1') ? 1 : 0;
|
||||
|
||||
if ($contact_id) { // Existing contact
|
||||
$sql = "UPDATE customer_contacts SET name = ?, email = ?, phone = ?, is_primary = ? WHERE id = ? AND customer_application_id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$contact_data['name'], $contact_data['email'], $contact_data['phone'], $is_primary, $contact_id, $customer_id]);
|
||||
$submitted_contact_ids[] = $contact_id;
|
||||
} else { // New contact
|
||||
$sql = "INSERT INTO customer_contacts (customer_application_id, name, email, phone, is_primary) VALUES (?, ?, ?, ?, ?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$customer_id, $contact_data['name'], $contact_data['email'], $contact_data['phone'], $is_primary]);
|
||||
$submitted_contact_ids[] = $pdo->lastInsertId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Delete Removed Contacts
|
||||
$stmt = $pdo->prepare("SELECT id FROM customer_contacts WHERE customer_application_id = ?");
|
||||
$stmt->execute([$customer_id]);
|
||||
$existing_contact_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$contacts_to_delete = array_diff($existing_contact_ids, $submitted_contact_ids);
|
||||
|
||||
if (!empty($contacts_to_delete)) {
|
||||
$sql = "DELETE FROM customer_contacts WHERE id IN (" . implode(',', array_fill(0, count($contacts_to_delete), '?')) . ") AND customer_application_id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$params = array_merge(array_values($contacts_to_delete), [$customer_id]);
|
||||
$stmt->execute($params);
|
||||
}
|
||||
|
||||
// 4. Process Addresses
|
||||
$submitted_address_ids = [];
|
||||
if (isset($_POST['address']) && is_array($_POST['address'])) {
|
||||
foreach ($_POST['address'] as $address_data) {
|
||||
$address_id = $address_data['id'] ?? null;
|
||||
|
||||
if ($address_id) { // Existing address
|
||||
$sql = "UPDATE customer_addresses SET address_type = ?, address_line_1 = ?, address_line_2 = ?, city = ?, state = ?, postal_code = ?, country = ? WHERE id = ? AND customer_application_id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$address_data['type'], $address_data['line1'], $address_data['line2'], $address_data['city'], $address_data['state'], $address_data['postal_code'], $address_data['country'], $address_id, $customer_id]);
|
||||
$submitted_address_ids[] = $address_id;
|
||||
} else { // New address
|
||||
$sql = "INSERT INTO customer_addresses (customer_application_id, address_type, address_line_1, address_line_2, city, state, postal_code, country) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$customer_id, $address_data['type'], $address_data['line1'], $address_data['line2'], $address_data['city'], $address_data['state'], $address_data['postal_code'], $address_data['country']]);
|
||||
$submitted_address_ids[] = $pdo->lastInsertId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Delete Removed Addresses
|
||||
$stmt = $pdo->prepare("SELECT id FROM customer_addresses WHERE customer_application_id = ?");
|
||||
$stmt->execute([$customer_id]);
|
||||
$existing_address_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$addresses_to_delete = array_diff($existing_address_ids, $submitted_address_ids);
|
||||
|
||||
if (!empty($addresses_to_delete)) {
|
||||
$sql = "DELETE FROM customer_addresses WHERE id IN (" . implode(',', array_fill(0, count($addresses_to_delete), '?')) . ") AND customer_application_id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$params = array_merge(array_values($addresses_to_delete), [$customer_id]);
|
||||
$stmt->execute($params);
|
||||
}
|
||||
|
||||
// Check if the application was reverted and resubmit it
|
||||
$stmt_status = $pdo->prepare("SELECT status FROM customer_applications WHERE id = ?");
|
||||
$stmt_status->execute([$customer_id]);
|
||||
$current_status = $stmt_status->fetchColumn();
|
||||
|
||||
if ($current_status === 'REVERTED') {
|
||||
$stmt_resubmit = $pdo->prepare("UPDATE customer_applications SET status = 'pending_approval' WHERE id = ?");
|
||||
$stmt_resubmit->execute([$customer_id]);
|
||||
$_SESSION['message'] = "Application resubmitted for approval.";
|
||||
$_SESSION['message_type'] = 'success';
|
||||
} else {
|
||||
$_SESSION['message'] = "Application #{$customer_id} updated successfully.";
|
||||
$_SESSION['message_type'] = 'success';
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
$_SESSION['message'] = "Error updating application: " . $e->getMessage();
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
}
|
||||
|
||||
header("Location: view_application.php?id=" . $customer_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
67
update_profile.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/auth_helpers.php';
|
||||
|
||||
// Protect route: check if user is logged in
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$current_password = $_POST['current_password'] ?? '';
|
||||
$new_password = $_POST['new_password'] ?? '';
|
||||
$confirm_new_password = $_POST['confirm_new_password'] ?? '';
|
||||
|
||||
// 1. Validate inputs
|
||||
if (empty($current_password) || empty($new_password) || empty($confirm_new_password)) {
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'All fields are required.'];
|
||||
header('Location: profile.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($new_password !== $confirm_new_password) {
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'New passwords do not match.'];
|
||||
header('Location: profile.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// 2. Fetch current user from DB to verify current password
|
||||
$stmt = $pdo->prepare("SELECT password FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user']['id']]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$user || !password_verify($current_password, $user['password'])) {
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Incorrect current password.'];
|
||||
header('Location: profile.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// 3. Hash new password
|
||||
$new_password_hash = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
|
||||
// 4. Update password in the database
|
||||
$stmt = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
$stmt->execute([$new_password_hash, $_SESSION['user']['id']]);
|
||||
|
||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'Password updated successfully.'];
|
||||
header('Location: profile.php');
|
||||
exit();
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error and show a generic message
|
||||
error_log("Password update failed: " . $e->getMessage());
|
||||
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'An error occurred. Please try again.'];
|
||||
header('Location: profile.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Redirect if not a POST request
|
||||
header('Location: profile.php');
|
||||
exit();
|
||||
}
|
||||
105
update_status.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
redirect_if_not_authenticated();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
exit('Method Not Allowed');
|
||||
}
|
||||
|
||||
$application_id = $_POST['application_id'] ?? null;
|
||||
$new_status = $_POST['status'] ?? null;
|
||||
$comments = $_POST['comments'] ?? '';
|
||||
|
||||
if (!$application_id || !in_array($new_status, ['approved', 'rejected', 'reverted'])) {
|
||||
$_SESSION['message'] = 'Invalid request.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
try {
|
||||
// Get current application details
|
||||
$stmt_app = $pdo->prepare("SELECT approval_level, current_approver_role_id FROM customer_applications WHERE id = ?");
|
||||
$stmt_app->execute([$application_id]);
|
||||
$application = $stmt_app->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$application) {
|
||||
$_SESSION['message'] = 'Application not found.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$current_level = $application['approval_level'];
|
||||
$current_approver_role_id = $application['current_approver_role_id'];
|
||||
$user_role_id = $_SESSION['user']['role_id'];
|
||||
|
||||
// Check if the user has permission to approve at this level
|
||||
if ($user_role_id != $current_approver_role_id) {
|
||||
$_SESSION['message'] = 'You do not have permission to approve this application at the current level.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Insert into application_approvals
|
||||
$stmt_approval = $pdo->prepare(
|
||||
'INSERT INTO application_approvals (application_id, approver_id, approval_level, status, comments) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
$stmt_approval->execute([$application_id, $_SESSION['user']['id'], $current_level, $new_status, $comments]);
|
||||
|
||||
if ($new_status === 'approved') {
|
||||
$next_level = $current_level + 1;
|
||||
if ($next_level <= 7) {
|
||||
// Get the role ID for the next approval level
|
||||
$stmt_role = $pdo->prepare("SELECT id FROM roles WHERE name = ?");
|
||||
$stmt_role->execute(['Approver Level ' . $next_level]);
|
||||
$next_approver_role = $stmt_role->fetch(PDO::FETCH_ASSOC);
|
||||
$next_approver_role_id = $next_approver_role ? $next_approver_role['id'] : null;
|
||||
|
||||
$stmt_update = $pdo->prepare(
|
||||
'UPDATE customer_applications SET approval_level = ?, current_approver_role_id = ? WHERE id = ?'
|
||||
);
|
||||
$stmt_update->execute([$next_level, $next_approver_role_id, $application_id]);
|
||||
$_SESSION['message'] = "Application approved and moved to the next level.";
|
||||
|
||||
} else {
|
||||
$stmt_update = $pdo->prepare(
|
||||
"UPDATE customer_applications SET status = 'APPROVED', approval_level = 7, current_approver_role_id = NULL WHERE id = ?"
|
||||
);
|
||||
$stmt_update->execute([$application_id]);
|
||||
$_SESSION['message'] = "Application approved.";
|
||||
}
|
||||
} elseif ($new_status === 'reverted') { // Reverted
|
||||
$stmt_update = $pdo->prepare(
|
||||
"UPDATE customer_applications SET status = 'REVERTED' WHERE id = ?"
|
||||
);
|
||||
$stmt_update->execute([$application_id]);
|
||||
$_SESSION['message'] = "Application reverted to the applicant for amendments.";
|
||||
} else { // Rejected
|
||||
$stmt_update = $pdo->prepare(
|
||||
"UPDATE customer_applications SET status = 'REJECTED', current_approver_role_id = NULL WHERE id = ?"
|
||||
);
|
||||
$stmt_update->execute([$application_id]);
|
||||
$_SESSION['message'] = "Application rejected.";
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
$_SESSION['message_type'] = 'success';
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
$_SESSION['message'] = 'Database error: ' . $e->getMessage();
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
}
|
||||
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
72
upload_file.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
redirect_if_not_authenticated();
|
||||
redirect_if_no_permission('upload_files');
|
||||
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$application_id = $_POST['application_id'] ?? null;
|
||||
|
||||
if (!$application_id) {
|
||||
$_SESSION['message'] = 'Invalid application ID.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if (isset($_FILES['document']) && $_FILES['document']['error'] === UPLOAD_ERR_OK) {
|
||||
$file = $_FILES['document'];
|
||||
$original_filename = basename($file['name']);
|
||||
$stored_filename = uniqid('', true) . '-' . $original_filename;
|
||||
$upload_dir = __DIR__ . '/uploads/';
|
||||
$upload_path = $upload_dir . $stored_filename;
|
||||
|
||||
// Basic validation (you can add more)
|
||||
$allowed_types = ['application/pdf', 'image/jpeg', 'image/png', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
|
||||
if (!in_array($file['type'], $allowed_types)) {
|
||||
$_SESSION['message'] = 'Invalid file type. Allowed types: PDF, JPG, PNG, DOC, DOCX.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($file['size'] > 5 * 1024 * 1024) { // 5MB limit
|
||||
$_SESSION['message'] = 'File is too large. Maximum size is 5MB.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (move_uploaded_file($file['tmp_name'], $upload_path)) {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("INSERT INTO application_files (application_id, original_filename, stored_filename) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$application_id, $original_filename, $stored_filename]);
|
||||
|
||||
$_SESSION['message'] = 'File uploaded successfully.';
|
||||
$_SESSION['message_type'] = 'success';
|
||||
} catch (PDOException $e) {
|
||||
// In a real app, log this error
|
||||
$_SESSION['message'] = 'Database error while saving file information.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
// Optionally, delete the uploaded file if DB insert fails
|
||||
unlink($upload_path);
|
||||
}
|
||||
} else {
|
||||
$_SESSION['message'] = 'Failed to move uploaded file.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
}
|
||||
} else {
|
||||
$_SESSION['message'] = 'File upload error. Please try again.';
|
||||
$_SESSION['message_type'] = 'danger';
|
||||
}
|
||||
|
||||
header('Location: view_application.php?id=' . $application_id);
|
||||
exit();
|
||||
|
||||
} else {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
1
uploads/.htaccess
Normal file
@ -0,0 +1 @@
|
||||
deny from all
|
||||
192
view_application.php
Normal file
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require_once 'includes/auth_helpers.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
redirect_if_not_authenticated();
|
||||
|
||||
if (!isset($_GET['id']) || empty($_GET['id'])) {
|
||||
header('Location: view_applications.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$application_id = $_GET['id'];
|
||||
$pdo = db();
|
||||
|
||||
// --- New Approval Workflow ---
|
||||
$approval_levels = [
|
||||
1 => 'Sales Manager',
|
||||
2 => 'General Manager',
|
||||
3 => 'Managing Director',
|
||||
4 => 'Accounts',
|
||||
5 => 'IT'
|
||||
];
|
||||
// --- End New Approval Workflow ---
|
||||
|
||||
// Fetch application details
|
||||
$stmt = $pdo->prepare('SELECT a.*, r.name as current_approver_role FROM customer_applications a LEFT JOIN roles r ON a.current_approver_role_id = r.id WHERE a.id = ?');
|
||||
$stmt->execute([$application_id]);
|
||||
$application = $stmt->fetch();
|
||||
|
||||
if (!$application) {
|
||||
die('Application not found.');
|
||||
}
|
||||
|
||||
// Fetch approval history
|
||||
$stmt_history = $pdo->prepare('SELECT ah.*, u.username as approver_name FROM application_approvals ah JOIN users u ON ah.approver_id = u.id WHERE ah.application_id = ? ORDER BY ah.created_at DESC');
|
||||
$stmt_history->execute([$application_id]);
|
||||
$approval_history = $stmt_history->fetchAll();
|
||||
|
||||
// Fetch other details (contacts, addresses, etc.) - condensed for brevity
|
||||
$contacts = $pdo->query("SELECT * FROM customer_contacts WHERE customer_application_id = {$application_id}")->fetchAll();
|
||||
$addresses = $pdo->query("SELECT * FROM customer_addresses WHERE customer_application_id = {$application_id}")->fetchAll();
|
||||
$trade_references = $pdo->query("SELECT * FROM customer_trade_references WHERE customer_application_id = {$application_id}")->fetchAll();
|
||||
$bank_details = $pdo->query("SELECT * FROM customer_bank_details WHERE customer_application_id = {$application_id}")->fetch();
|
||||
$principals = $pdo->query("SELECT * FROM customer_principals WHERE customer_application_id = {$application_id}")->fetchAll();
|
||||
|
||||
// Get current user role
|
||||
$current_user_role_id = get_user_role_id();
|
||||
$stmt_role = $pdo->prepare("SELECT name FROM roles WHERE id = ?");
|
||||
$stmt_role->execute([$current_user_role_id]);
|
||||
$current_user_role_name = $stmt_role->fetchColumn();
|
||||
|
||||
$current_level = $application['approval_level'];
|
||||
$required_role = $approval_levels[$current_level] ?? null;
|
||||
$can_approve = ($current_user_role_name === $required_role || $current_user_role_name === 'admin');
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>View Application - <?= htmlspecialchars($application['application_id']) ?></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Customer Master</a>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php">Dashboard</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="view_applications.php">View Applications</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<?php if (isset($_SESSION['flash_message'])): ?>
|
||||
<div class="alert alert-<?= $_SESSION['flash_message']['type'] ?> alert-dismissible fade show" role="alert">
|
||||
<?= $_SESSION['flash_message']['message'] ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['flash_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>Application Details: <?= htmlspecialchars($application['application_id']) ?></h2>
|
||||
|
||||
<!-- Approval Status & History -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Approval Status & History</div>
|
||||
<div class="card-body">
|
||||
<h4>Current Status:
|
||||
<span class="badge bg-<?=
|
||||
strtoupper($application['status']) === 'APPROVED' ? 'success' : (
|
||||
strtoupper($application['status']) === 'REJECTED' ? 'danger' : (
|
||||
strtoupper($application['status']) === 'RETURNED' ? 'warning' : 'secondary'
|
||||
))
|
||||
?>">
|
||||
<?= htmlspecialchars(strtoupper($application['status'])) ?>
|
||||
</span>
|
||||
</h4>
|
||||
<?php if ($application['status'] === 'Pending' && $application['current_approver_role']): ?>
|
||||
<p class="text-muted">Waiting for approval from: <strong><?= htmlspecialchars($application['current_approver_role']) ?></strong></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($application['status'] === 'Draft'): ?>
|
||||
<form action="submit_for_approval.php" method="POST" class="mt-3">
|
||||
<input type="hidden" name="application_id" value="<?= $application['id'] ?>">
|
||||
<button type="submit" class="btn btn-primary">Submit for Approval</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<h5 class="mt-4">Approval History</h5>
|
||||
<?php if (empty($approval_history)): ?>
|
||||
<p>No approval history found.</p>
|
||||
<?php else: ?>
|
||||
<ul class="list-group">
|
||||
<?php foreach ($approval_history as $history): ?>
|
||||
<li class="list-group-item">
|
||||
<p class="mb-1"><strong>Action:</strong> <?= htmlspecialchars($history['status']) ?></p>
|
||||
<p class="mb-1"><strong>By:</strong> <?= htmlspecialchars($history['approver_name']) ?></p>
|
||||
<?php if (!empty($history['comments'])): ?>
|
||||
<p class="mb-1"><strong>Comments:</strong> <?= nl2br(htmlspecialchars($history['comments'])) ?></p>
|
||||
<?php endif; ?>
|
||||
<small class="text-muted"><?= date('Y-m-d H:i:s', strtotime($history['created_at'])) ?></small>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Approval Action Form -->
|
||||
<?php if ($can_approve && in_array($application['status'], ['Pending', 'Returned'])): ?>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Approval Action</div>
|
||||
<div class="card-body">
|
||||
<form action="process_approval.php" method="POST">
|
||||
<input type="hidden" name="application_id" value="<?= $application['id'] ?>">
|
||||
<div class="mb-3">
|
||||
<label for="comments" class="form-label">Comments (Required for Return/Reject)</label>
|
||||
<textarea class="form-control" id="comments" name="comments" rows="3"></textarea>
|
||||
</div>
|
||||
<button type="submit" name="action" value="approve" class="btn btn-success">Approve</button>
|
||||
<?php if ($current_level > 1): // Cannot return from the first level ?>
|
||||
<button type="submit" name="action" value="return" class="btn btn-warning">Return</button>
|
||||
<?php endif; ?>
|
||||
<button type="submit" name="action" value="reject" class="btn btn-danger">Reject</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Tabs for Application Details -->
|
||||
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="company-tab" data-bs-toggle="tab" data-bs-target="#company" type="button" role="tab" aria-controls="company" aria-selected="true">Company</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="summary-tab" data-bs-toggle="tab" data-bs-target="#summary" type="button" role="tab" aria-controls="summary" aria-selected="false">Summary</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-pane fade show active" id="company" role="tabpanel" aria-labelledby="company-tab">
|
||||
<!-- Company Details -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Company Details</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Company Name:</strong> <?= htmlspecialchars($application['company_name']) ?></p>
|
||||
<p><strong>Company Website:</strong> <a href="<?= htmlspecialchars($application['company_website']) ?>" target="_blank"><?= htmlspecialchars($application['company_website']) ?></a></p>
|
||||
<p><strong>Company Phone:</strong> <?= htmlspecialchars($application['company_phone']) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="summary" role="tabpanel" aria-labelledby="summary-tab">
|
||||
<!-- Application Summary -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Application Summary</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Test:</strong> Test</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
71
view_applications.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'includes/auth_helpers.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
redirect_if_not_authenticated();
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query('SELECT id, application_id, company_name, status, created_at FROM customer_applications ORDER BY created_at DESC');
|
||||
$applications = $stmt->fetchAll();
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>View Applications</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Customer Master</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="logout.php">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mt-4">
|
||||
<h2>Submitted Applications</h2>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Application ID</th>
|
||||
<th>Company Name</th>
|
||||
<th>Status</th>
|
||||
<th>Submitted At</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($applications)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">No applications found.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($applications as $app): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($app['application_id']) ?></td>
|
||||
<td><?= htmlspecialchars($app['company_name']) ?></td>
|
||||
<td><?= htmlspecialchars($app['status']) ?></td>
|
||||
<td><?= htmlspecialchars($app['created_at']) ?></td>
|
||||
<td>
|
||||
<a href="view_application.php?id=<?= $app['id'] ?>" class="btn btn-primary btn-sm">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||