FEAT_COMMISSION_UPLOAD
This commit is contained in:
parent
6baf24bc26
commit
f442de5271
@ -35,6 +35,7 @@ $routes->get("updateRenewalDataNotExistingClient", "ClientController::updateRene
|
|||||||
$routes->get("updateRenewalInsurerData", "ClientController::updateRenewalInsurerData");
|
$routes->get("updateRenewalInsurerData", "ClientController::updateRenewalInsurerData");
|
||||||
$routes->get("sendMutipleToEmails", "MasterController::sendMutipleToEmails");
|
$routes->get("sendMutipleToEmails", "MasterController::sendMutipleToEmails");
|
||||||
$routes->post("getCommission", "InsuranceCommissionController::initiateCommissionCalc",['filter' => 'CommissionApiFilter']);
|
$routes->post("getCommission", "InsuranceCommissionController::initiateCommissionCalc",['filter' => 'CommissionApiFilter']);
|
||||||
|
$routes->get("importRules", "RuleImportController::upload");
|
||||||
// $routes->post("iAgreeForAddOn", "EmployeeRestController::iAgreeForAddOn");
|
// $routes->post("iAgreeForAddOn", "EmployeeRestController::iAgreeForAddOn");
|
||||||
// $routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
|
// $routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
|
||||||
// $routes->post("employeeUpload", "EmployeeRestController::employeeUpload");
|
// $routes->post("employeeUpload", "EmployeeRestController::employeeUpload");
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use App\Libraries\Slug;
|
|||||||
use App\Libraries\MyLogger;
|
use App\Libraries\MyLogger;
|
||||||
use App\Libraries\GmailAPI;
|
use App\Libraries\GmailAPI;
|
||||||
use App\Libraries\MyGoogleDrive;
|
use App\Libraries\MyGoogleDrive;
|
||||||
|
use App\Libraries\RuleImportService;
|
||||||
use App\Libraries\DataServiceSqlite;
|
use App\Libraries\DataServiceSqlite;
|
||||||
use App\Controllers\Home;
|
use App\Controllers\Home;
|
||||||
|
|
||||||
@ -80,5 +81,14 @@ class Services extends BaseService
|
|||||||
|
|
||||||
return new MyGoogleDrive();
|
return new MyGoogleDrive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function ruleImportService($getShared = true)
|
||||||
|
{
|
||||||
|
if ($getShared) {
|
||||||
|
return static::getSharedInstance('ruleImportService');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RuleImportService();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
350
app/Controllers/RuleImportController.php
Normal file
350
app/Controllers/RuleImportController.php
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
class RuleImportController extends AdminController
|
||||||
|
{
|
||||||
|
protected $ruleImportService;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->ruleImportService = \Config\Services::ruleImportService();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload endpoint for form (POST)
|
||||||
|
* Input form field: 'rules_file'
|
||||||
|
*/
|
||||||
|
public function uploadORI()
|
||||||
|
{
|
||||||
|
// echo 'hi';
|
||||||
|
!dd($result = $this->ruleImportService->processUpload(['id' => 1,'file_name' => 'sample_commission.csv','insurer_id' => 5, 'department' => 'motor' ,'commission_month' => '2025-11-10']));die;
|
||||||
|
try {
|
||||||
|
$file = $this->request->getFile('rules_file');
|
||||||
|
if (!$file || !$file->isValid()) {
|
||||||
|
return $this->response->setJSON(['status'=>'error','message'=>'No file uploaded or upload error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move uploaded file to writable temp location
|
||||||
|
$tmpPath = WRITEPATH . 'uploads/' . $file->getRandomName();
|
||||||
|
$file->move(WRITEPATH . 'uploads', $file->getName()); // keep original name inside uploads
|
||||||
|
$uploadedFullPath = $file->getTempName(); // Note: CI may store in tmp; we will use moved file path instead
|
||||||
|
$movedPath = WRITEPATH . 'uploads/' . $file->getName();
|
||||||
|
|
||||||
|
// Process file
|
||||||
|
$result = $this->ruleImportService->processUpload($movedPath, $file->getName());
|
||||||
|
|
||||||
|
// Return JSON with annotated file link if present
|
||||||
|
if (isset($result['annotated_file']) && $result['annotated_file']) {
|
||||||
|
$annotUrl = base_url('writable/uploads/annotated/' . basename($result['annotated_file']));
|
||||||
|
$result['annotated_url'] = $annotUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON($result);
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
log_message('critical', 'RuleImportController::upload ' . $e->getMessage());
|
||||||
|
return $this->response->setJSON(['status'=>'exception','message'=>$e->getMessage()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function upload()
|
||||||
|
{
|
||||||
|
// Make sure filesystem helpers available if you need them
|
||||||
|
helper(['filesystem']);
|
||||||
|
|
||||||
|
// CommissionFilesModel – adjust namespace if different
|
||||||
|
$commissionFilesModel = new \App\Models\CommissionFilesModel();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 1. Get uploaded file
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
$file = $this->request->getFile('rules_file');
|
||||||
|
if (!$file || !$file->isValid()) {
|
||||||
|
log_message('warning', 'RuleImportController::upload - No file or invalid upload.');
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'No file uploaded or upload error.'
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 2. Read POST fields
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
$insurerId = $this->request->getPost('insurer_id');
|
||||||
|
$department = $this->request->getPost('department');
|
||||||
|
$commissionMonth = $this->request->getPost('commission_month');
|
||||||
|
$createdBy = $this->request->getPost('created_by');
|
||||||
|
|
||||||
|
if (empty($insurerId) || empty($department) || empty($commissionMonth) || empty($createdBy)) {
|
||||||
|
log_message('warning', 'RuleImportController::upload - Missing required POST data.', [
|
||||||
|
'insurer_id' => $insurerId,
|
||||||
|
'department' => $department,
|
||||||
|
'commission_month' => $commissionMonth,
|
||||||
|
'created_by' => $createdBy,
|
||||||
|
]);
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Missing required fields: insurer_id, department, commission_month, created_by.'
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional / default fields
|
||||||
|
$postedFileName = $this->request->getPost('file_name') ?: $file->getClientName();
|
||||||
|
$fileStatus = $this->request->getPost('file_status') ?: 'pending';
|
||||||
|
$isActive = $this->request->getPost('is_active') !== null ? (int)$this->request->getPost('is_active') : 1;
|
||||||
|
// rules_count is given by user but we will override it after processing on success
|
||||||
|
$postedRulesCount = $this->request->getPost('rules_count') !== null
|
||||||
|
? (int)$this->request->getPost('rules_count')
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 3. Move file to WRITEPATH/uploads/commission/files using user filename
|
||||||
|
// (no random name as per your requirement)
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
$uploadDir = WRITEPATH . 'uploads/commission/files/';
|
||||||
|
if (!is_dir($uploadDir)) {
|
||||||
|
if (!mkdir($uploadDir, 0755, true) && !is_dir($uploadDir)) {
|
||||||
|
throw new \RuntimeException("Failed to create upload directory: {$uploadDir}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitize user file name but keep it deterministic (no random, no timestamp)
|
||||||
|
$safeName = preg_replace('/[^a-zA-Z0-9_\-\.]/', '_', $postedFileName);
|
||||||
|
$targetFileName = $safeName;
|
||||||
|
$movedFullPath = $uploadDir . $targetFileName;
|
||||||
|
|
||||||
|
$file->move($uploadDir, $targetFileName);
|
||||||
|
if (!file_exists($movedFullPath)) {
|
||||||
|
log_message('error', "RuleImportController::upload - Failed to move uploaded file to {$movedFullPath}");
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Failed to store uploaded file.'
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('info', "RuleImportController::upload - File moved to {$movedFullPath}");
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 4. Insert commission_files row with status pending
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
$insertData = [
|
||||||
|
'file_name' => $targetFileName,
|
||||||
|
'insurer_id' => (int)$insurerId,
|
||||||
|
'department' => $department,
|
||||||
|
'commission_month'=> $commissionMonth,
|
||||||
|
'rules_count' => 0, // will update on success
|
||||||
|
'file_status' => $fileStatus, // 'pending' by default
|
||||||
|
'is_active' => $isActive,
|
||||||
|
'created_by' => (int)$createdBy,
|
||||||
|
'created_at' => date('Y-m-d H:i:s'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$commissionFilesModel->insert($insertData);
|
||||||
|
$insertId = $commissionFilesModel->getInsertID();
|
||||||
|
|
||||||
|
if (empty($insertId)) {
|
||||||
|
log_message('error', 'RuleImportController::upload - Failed to insert commission_files record', ['data' => $insertData]);
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Failed to record upload in database.'
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('info', "RuleImportController::upload - commission_files inserted id={$insertId}", $insertData);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 5. Call ruleImportService->processUpload with inserted file info
|
||||||
|
// As per your spec:
|
||||||
|
// $this->ruleImportService->processUpload([
|
||||||
|
// 'id' => 1,
|
||||||
|
// 'file_name' => 'sample_commission.csv',
|
||||||
|
// 'insurer_id' => 5,
|
||||||
|
// 'department' => 'motor',
|
||||||
|
// 'commission_month' => '2025-11-10'
|
||||||
|
// ])
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
$payload = [
|
||||||
|
'id' => (int)$insertId,
|
||||||
|
'file_name' => $targetFileName,
|
||||||
|
'insurer_id' => (int)$insurerId,
|
||||||
|
'department' => $department,
|
||||||
|
'commission_month' => $commissionMonth,
|
||||||
|
];
|
||||||
|
|
||||||
|
log_message('info', 'RuleImportController::upload - Calling ruleImportService->processUpload', ['payload' => $payload]);
|
||||||
|
|
||||||
|
$result = $this->ruleImportService->processUpload($payload);
|
||||||
|
|
||||||
|
if (!is_array($result) || !isset($result['status'])) {
|
||||||
|
log_message('error', 'RuleImportController::upload - Invalid service response', ['response' => $result]);
|
||||||
|
// update file status as failed
|
||||||
|
$commissionFilesModel->update($insertId, [
|
||||||
|
'file_status' => 'failed',
|
||||||
|
'updated_by' => (int)$createdBy,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Invalid response from import service.'
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 6. Handle SUCCESS
|
||||||
|
// - result['rules'] exists
|
||||||
|
// - result['errors'] empty
|
||||||
|
// - NO annotated_file
|
||||||
|
// - Save rules as JSON in WRITEPATH/uploads/commission/json/{insurer_id}_{department}.json
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
if ($result['status'] === 'success') {
|
||||||
|
$rulesArray = isset($result['rules']) && is_array($result['rules']) ? $result['rules'] : [];
|
||||||
|
$rulesCount = count($rulesArray);
|
||||||
|
|
||||||
|
// Save JSON to WRITEPATH . 'uploads/commission/json/{insurer_id}_{department}.json'
|
||||||
|
$jsonDir = WRITEPATH . 'uploads/commission/json/';
|
||||||
|
if (!is_dir($jsonDir)) {
|
||||||
|
if (!mkdir($jsonDir, 0755, true) && !is_dir($jsonDir)) {
|
||||||
|
throw new \RuntimeException("Failed to create JSON output directory: {$jsonDir}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$deptSlug = preg_replace('/[^a-zA-Z0-9_\-]/', '_', strtolower($department));
|
||||||
|
$jsonName = (int)$insurerId . '_' . $deptSlug . '.json';
|
||||||
|
$jsonPath = $jsonDir . $jsonName;
|
||||||
|
|
||||||
|
$jsonData = json_encode($rulesArray, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
|
if ($jsonData === false) {
|
||||||
|
log_message('error', 'RuleImportController::upload - json_encode failed for rules', [
|
||||||
|
'last_error' => json_last_error_msg()
|
||||||
|
]);
|
||||||
|
// mark as failed since we cannot save rules
|
||||||
|
$commissionFilesModel->update($insertId, [
|
||||||
|
'file_status' => 'failed',
|
||||||
|
'updated_by' => (int)$createdBy,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Failed to encode rules as JSON.'
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_put_contents($jsonPath, $jsonData) === false) {
|
||||||
|
log_message('error', 'RuleImportController::upload - Failed to write rules JSON file', ['json_path' => $jsonPath]);
|
||||||
|
$commissionFilesModel->update($insertId, [
|
||||||
|
'file_status' => 'failed',
|
||||||
|
'updated_by' => (int)$createdBy,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Failed to store rules JSON file.'
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('info', 'RuleImportController::upload - Rules JSON written', [
|
||||||
|
'file_id' => $insertId,
|
||||||
|
'json_path' => $jsonPath,
|
||||||
|
'rules_cnt' => $rulesCount,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Update DB: status, rules_count, updated_by
|
||||||
|
$commissionFilesModel->update($insertId, [
|
||||||
|
'file_status' => 'processed',
|
||||||
|
'rules_count' => $rulesCount,
|
||||||
|
'updated_by' => (int)$createdBy,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
// If you have a column for JSON path, uncomment:
|
||||||
|
// 'json_file_path' => $jsonPath,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'File processed successfully.',
|
||||||
|
'file_id' => $insertId,
|
||||||
|
'rules_count' => $rulesCount,
|
||||||
|
'json_file' => $jsonPath,
|
||||||
|
'service' => $result, // optional: return full service response if you want
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 7. Handle ERROR (validation failed etc.)
|
||||||
|
// - Do NOT save any rules JSON
|
||||||
|
// - Update file_status to validation_failed
|
||||||
|
// - Store annotated_file path if you have such a column
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
if ($result['status'] === 'error') {
|
||||||
|
$annotatedPath = $result['annotated_file'] ?? null;
|
||||||
|
$errors = $result['errors'] ?? [];
|
||||||
|
|
||||||
|
$updateData = [
|
||||||
|
'file_status' => 'validation_failed',
|
||||||
|
'rules_count' => 0, // do not save rules
|
||||||
|
'updated_by' => (int)$createdBy,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// If you have a column for annotated file path, e.g. annotated_file_path
|
||||||
|
if ($annotatedPath) {
|
||||||
|
$updateData['annotated_file_path'] = $annotatedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commissionFilesModel->update($insertId, $updateData);
|
||||||
|
|
||||||
|
log_message('warning', "RuleImportController::upload - Validation failed for file_id={$insertId}", [
|
||||||
|
'errors' => $errors,
|
||||||
|
'annotated_file' => $annotatedPath
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Validation failed. No rules saved.',
|
||||||
|
'file_id' => $insertId,
|
||||||
|
'errors' => $errors,
|
||||||
|
'annotated_file' => $annotatedPath,
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 8. Unexpected status
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
log_message('error', 'RuleImportController::upload - Unexpected result status from service', ['result' => $result]);
|
||||||
|
$commissionFilesModel->update($insertId, [
|
||||||
|
'file_status' => 'failed',
|
||||||
|
'updated_by' => (int)$createdBy,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Unexpected import service result.'
|
||||||
|
], 500);
|
||||||
|
|
||||||
|
} catch (\Throwable $ex) {
|
||||||
|
log_message('critical', 'RuleImportController::upload exception: ' . $ex->getMessage(), [
|
||||||
|
'trace' => $ex->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Try to update the commission_files record if insertId exists
|
||||||
|
if (isset($insertId) && !empty($insertId)) {
|
||||||
|
try {
|
||||||
|
$commissionFilesModel->update($insertId, [
|
||||||
|
'file_status' => 'failed',
|
||||||
|
'updated_by' => isset($createdBy) ? (int)$createdBy : null,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
'notes' => 'Upload exception: ' . $ex->getMessage(),
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e2) {
|
||||||
|
log_message('error', 'RuleImportController::upload - failed to update commission_files after exception: ' . $e2->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respond([
|
||||||
|
'status' => 'exception',
|
||||||
|
'message' => $ex->getMessage(),
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1373
app/Libraries/RuleImportService.php
Normal file
1373
app/Libraries/RuleImportService.php
Normal file
File diff suppressed because it is too large
Load Diff
67
app/Models/CommissionFilesModel.php
Normal file
67
app/Models/CommissionFilesModel.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class CommissionFilesModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'commission_files';
|
||||||
|
protected $primaryKey = 'id';
|
||||||
|
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useSoftDeletes = false;
|
||||||
|
|
||||||
|
protected $allowedFields = [
|
||||||
|
'file_name',
|
||||||
|
'insurer_id',
|
||||||
|
'department',
|
||||||
|
'commission_month',
|
||||||
|
'file_status',
|
||||||
|
'is_active',
|
||||||
|
'created_by',
|
||||||
|
'updated_by',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Auto timestamps by CI4
|
||||||
|
protected $useTimestamps = true;
|
||||||
|
protected $createdField = 'created_at';
|
||||||
|
protected $updatedField = 'updated_at';
|
||||||
|
|
||||||
|
// Validation rules (optional)
|
||||||
|
// protected $validationRules = [
|
||||||
|
// 'file_name' => 'required|min_length[1]|max_length[100]',
|
||||||
|
// 'insurer_id' => 'permit_empty|integer',
|
||||||
|
// 'department' => 'permit_empty|max_length[45]',
|
||||||
|
// 'commission_month' => 'permit_empty|valid_date',
|
||||||
|
// 'file_status' => 'permit_empty|max_length[10]',
|
||||||
|
// 'is_active' => 'permit_empty|in_list[0,1]'
|
||||||
|
// ];
|
||||||
|
|
||||||
|
protected $validationMessages = [];
|
||||||
|
protected $skipValidation = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get files with optional filters
|
||||||
|
*/
|
||||||
|
// public function getFiles($filters = [])
|
||||||
|
// {
|
||||||
|
// if (!empty($filters['insurer_id'])) {
|
||||||
|
// $this->where('insurer_id', $filters['insurer_id']);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!empty($filters['department'])) {
|
||||||
|
// $this->where('department', $filters['department']);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!empty($filters['file_status'])) {
|
||||||
|
// $this->where('file_status', $filters['file_status']);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (isset($filters['is_active'])) {
|
||||||
|
// $this->where('is_active', $filters['is_active']);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return $this->orderBy('id', 'DESC')->findAll();
|
||||||
|
// }
|
||||||
|
}
|
||||||
@ -95,6 +95,7 @@ p{
|
|||||||
background-image:url('<?= $client_logo ?>');
|
background-image:url('<?= $client_logo ?>');
|
||||||
background-size:contain;
|
background-size:contain;
|
||||||
background-repeat:no-repeat;
|
background-repeat:no-repeat;
|
||||||
|
background-position: center;
|
||||||
">
|
">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user