Hướng dẫn ghép code

Hướng dẫn tạo website

Trang này chứa các hướng dẫn chi tiết để tạo website và các bước cấu hình pages cho team code làm theo.

0. Cách tạo website

Quy trình thực tế để tạo website cho team code: đăng nhập hệ thống, tạo website mới, nhập domain, hệ thống tự tạo database theo domain, sau đó tải code về và cấu hình pages.

Bước 1: Đăng nhập hệ thống
  • Đăng nhập vào website quản trị/dashboard bằng tài khoản được cấp.
  • Kiểm tra tài khoản có quyền tạo website mới.
Bước 2: Thêm mới website
  • Vào menu: Quản lý hệ thống > Quản lý website.
  • Trong màn hình này có 2 chức năng theo nhu cầu người dùng:
    • Thêm mới web: dùng khi tạo website mới hoàn toàn.
    • Copy website: dùng khi cần nhân bản từ một web đã có.
  • Nhập domain/subdomain theo yêu cầu dự án rồi thực hiện chức năng tương ứng.
Bước 4: Tải code website về
  • Sau khi website được tạo thành công, bấm tải code từ thư mục Views trong source.
  • Đường dẫn thư mục views: /var/www/songtien.hdcode.name.vn/module/Frontend/Views.

1. Quản lý pages blocks

Tạo pages blocks cho website ngay sau khi tạo website xong:

  • Vào admin Pages của website đó.
  • Chọn page cần cấu hình và vào phần Pages Blocks.
  • Thêm block theo giao diện yêu cầu, sắp xếp bằng sort.
  • Lưu lại và kiểm tra hiển thị frontend ngay sau khi chỉnh.

2. Cài đặt website

Dưới đây là các biến và code cần thiết để cài đặt website:

Các thuộc tính của cài đặt website
Thuộc tính Kiểu dữ liệu Mô tả
idint(11)ID của cài đặt
namevarchar(255)Tên cài đặt. Sử dụng: <?php echo $configByWebsite['name'];?>
datatextDữ liệu cài đặt dạng text (đã được decode JSON thành array trong $configByWebsite). Chỉ cần sử dụng: <?php echo $configByWebsite['tên_thuộc_tính'];?> (tên_thuộc_tính chính là tên không dấu)
created_attimestampThời gian tạo (định dạng: Y-m-d H:i:s)
updated_attimestampThời gian cập nhật (định dạng: Y-m-d H:i:s)
user_insertint(11)ID người tạo
user_updateint(11)ID người cập nhật
data_codetextDữ liệu code bổ sung dạng text (chỉ dùng trong admin, không sử dụng ngoài frontend)
js_headertextCode JavaScript chèn vào phần header (đã được lấy ra trong meta.php). Sử dụng: <?php echo $configByWebsite['js_header'];?>
js_footertextCode JavaScript chèn vào phần footer (đã được lấy ra trong meta.php). Sử dụng: <?php echo $configByWebsite['js_footer'];?>
Tên logo
<?php echo $configByWebsite['name'];?>
Hình logo
<?php echo $this->getImageUrl($configByWebsite['logo']);?>
Thẻ meta
<?php 
        include VIEWS.'/default_fmax_website/layout/meta.php';
?>
Link css js
<?php echo URL_ASSETS_WEBSITE ?>

3. Lấy danh sách sản phẩm tin tức

Code để lấy items theo điều kiện:

Các thuộc tính của items
Thuộc tính Kiểu dữ liệu Mô tả
idint(11)ID của item
namevarchar(255)Tên item
slugvarchar(255)Đường dẫn URL của item
imagesvarchar(255)Hình ảnh chính của item (size: 300x300 - width x height). Sử dụng: <?php echo $this->getImageUrl($val['images'], '300x300');?> (không ghi size sẽ lấy hình gốc)
gallery_jsontextThư viện ảnh (size: text). Trả về array chứa danh sách tên hình. Sử dụng: <?php foreach($val['gallery_json'] as $img) { echo $this->getImageUrl($img, '300x300'); }?> (không ghi size sẽ lấy hình gốc)
descriptiontextMô tả ngắn của item
detaillongtextChi tiết đầy đủ của item
meta_titlevarchar(255)Tiêu đề SEO
meta_descriptionvarchar(300)Mô tả SEO
meta_keywordvarchar(300)Từ khóa SEO
fb_titlevarchar(255)Tiêu đề Facebook
fb_descriptionvarchar(300)Mô tả Facebook
fb_imagevarchar(255)Hình ảnh Facebook
category_idint(11)ID danh mục chứa item
user_insertint(11)ID người tạo
user_updateint(11)ID người cập nhật
id_editint(11)ID chỉnh sửa
statustinyint(1)Trạng thái (0/1)
created_attimestampThời gian tạo
updated_attimestampThời gian cập nhật
pint(11)Phân loại (dùng để lọc items. Ví dụ: if($val['p'] == 2)). Giá trị p chính là tham số sau URL trong admin, ví dụ: https://hdwebs.vn/admin/items/index?p=15
priceint(11)Giá gốc
price_promotionint(11)Giá khuyến mãi
checkbox1tinyint(1)Checkbox 1 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox1'] == 1))
checkbox2tinyint(1)Checkbox 2 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox2'] == 1))
checkbox3tinyint(1)Checkbox 3 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox3'] == 1))
checkbox4tinyint(1)Checkbox 4 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox4'] == 1))
sortint(11)Thứ tự sắp xếp
datatextDữ liệu bổ sung dạng text
multi_detaillongtextChi tiết đa dạng (JSON → array). Trên trang chi tiết, mỗi phần tử thường là HTML hoàn chỉnh: <?php foreach(($detail['multi_detail'] ?? []) as $item) { echo $item; }?> — xem mục 3.1. Không tách $item['title'] / $item['content'] nếu dữ liệu đã là chuỗi HTML.
Ví dụ sử dụng các thuộc tính items
<?php foreach($list_items as $val) {
    if($val['p'] == 2) {?>
    <div>
        <h3><?php echo $val['name'];?></h3>
        <img src="<?php echo $this->getImageUrl($val['images'], '300x300');?>" alt="<?php echo $val['name'];?>">
        <p><?php echo $val['description'];?></p>
        <div>
            <span>Giá: <?php echo number_format($val['price']);?> đ</span>
            <?php if($val['price_promotion'] > 0) {?>
                <span>Giá KM: <?php echo number_format($val['price_promotion']);?> đ</span>
            <?php }?>
        </div>
        <a href="<?php echo '/' . $val['slug'];?>">Xem chi tiết</a>
    </div>
<?php }
}?>
Lấy items có p = 2
<?php foreach($list_items as $val) {
    if($val['p'] == 2) {?>
    // Code hiển thị item ở đây
<?php }
}?>
Lấy items kết hợp nhiều điều kiện (p = 2 và checkbox1 = 1)
<?php 
    // Lấy items có p = 2 VÀ checkbox1 = 1
    foreach($list_items as $val) {
        if($val['p'] == 2 && $val['checkbox1'] == 1) {?>
        <div>
            <h3><?php echo $val['name'];?></h3>
            <!-- Code hiển thị item ở đây -->
        </div>
    <?php }
    }
?>
Danh sách có phân trang (lọc p = 2)
Giải thích code:
  • Bước 1 - Lọc dữ liệu: Tạo mảng tạm $list_items_temp và lọc các item có p == 2 từ mảng $list_items. Lưu ý: Giá trị p chính là tham số sau URL trong admin, ví dụ: https://hdwebs.vn/admin/items/index?p=15 (trong ví dụ này dùng p = 2)
  • Bước 2 - Thiết lập phân trang: Tạo mảng $option với các tham số:
    • 'data': Dữ liệu đã lọc để phân trang
    • 'baseUrl': URL cơ sở (slug của trang) để tạo link phân trang
    • 'perPage': Số item hiển thị trên mỗi trang (20 items/trang)
    • 'currentPage': Trang hiện tại, lấy từ $_GET['pages'] hoặc mặc định là 1
  • Bước 3 - Gọi hàm phân trang: Sử dụng $this->paginationFrontend($option) để tạo phân trang, kết quả trả về trong $list
  • Bước 4 - Hiển thị danh sách: Dùng vòng lặp foreach để duyệt qua $list['items'] và hiển thị từng item
  • Bước 5 - Hiển thị phân trang: In ra $list['pagination'] để hiển thị các nút chuyển trang
<?php 
    // Bước 1: Tạo mảng tạm để chứa các item đã lọc
    $list_items_temp = array();
    
    // Bước 2: Duyệt qua tất cả các item trong $list_items và lọc những item có p = 2
    // Lưu ý: Giá trị p chính là tham số sau URL trong admin, ví dụ: https://hdwebs.vn/admin/items/index?p=15
    foreach ($list_items as $val){
        if($val['p'] == 2){
            // Thêm item vào mảng tạm nếu thỏa điều kiện
            $list_items_temp[] = $val;
        }
    }
    
    // Bước 3: Thiết lập các tham số cho phân trang
    $option = array(
        'data' => $list_items_temp,              // Dữ liệu đã lọc để phân trang
        'baseUrl' => '/' . $pages['slug'],        // URL cơ sở (slug của trang) để tạo link phân trang
        'perPage' => 20,                         // Số item hiển thị trên mỗi trang (20 items/trang)
        'currentPage' => $_GET['pages'] ?? 1     // Trang hiện tại, lấy từ $_GET['pages'] hoặc mặc định là 1
    );
    
    // Bước 4: Gọi hàm phân trang và lưu kết quả vào $list
    $list = $this->paginationFrontend($option);
?>

<?php 
    // Bước 5: Duyệt qua các item đã được phân trang và hiển thị
    foreach ($list['items'] as $val){?>
    <!-- Code hiển thị item ở đây, ví dụ: -->
    <div>
        <!-- <?php echo $val['title'];?> -->
        <!-- <?php echo $val['content'];?> -->
    </div>
<?php }?>

<?php 
    // Bước 6: Hiển thị các nút phân trang (Previous, Next, số trang...)
    echo $list['pagination'];
?>

3.1. Chi tiết sản phẩm tin tức

Code để lấy và hiển thị chi tiết sản phẩm/tin tức:

Lấy chi tiết sản phẩm/tin tức
Lưu ý về multi_detail:

Trong phần Chi tiết đa dạng, biến $item đã chứa toàn bộ HTML của phần chi tiết (bao gồm cả title, content, và các thẻ HTML khác). Do đó, chỉ cần echo $item trực tiếp, không cần echo từng phần như $item['title'] hay $item['content'].

<?php if(!empty($detail)) {?>
    <div>
        <!-- Tên sản phẩm/tin tức -->
        <h1><?php echo $detail['name'];?></h1>
        
        <!-- Hình ảnh chính (size: 800x600) -->
        <img src="<?php echo $this->getImageUrl($detail['images'], '800x600');?>" alt="<?php echo $detail['name'];?>">
        
        <!-- Mô tả ngắn -->
        <p><?php echo $detail['description'];?></p>
        
        <!-- Chi tiết đầy đủ -->
        <div><?php echo $detail['detail'];?></div>
        
        <!-- Thư viện ảnh: Duyệt qua mảng gallery_json (mỗi $img là tên file ảnh) -->
        <?php if(!empty($detail['gallery_json'])) { ?>
            <?php foreach($detail['gallery_json'] as $img) { ?>
                <img src="<?php echo $this->getImageUrl($img, '800x600');?>" alt="">
            <?php } ?>
        <?php } ?>
        
        <!-- Chi tiết đa dạng: $item đã chứa toàn bộ HTML, chỉ cần echo trực tiếp (không cần echo $item['title'] hay $item['content']) -->
        <?php if(!empty($detail['multi_detail'])) { ?>
            <?php foreach($detail['multi_detail'] as $item) { ?>
                <?php echo $item;?>
            <?php } ?>
        <?php } ?>
        
        <!-- Giá sản phẩm (nếu có) -->
        <?php if(!empty($detail['price'])) { ?>
            <p>Giá: <?php echo number_format($detail['price']);?> đ</p>
            <!-- Giá khuyến mãi (nếu có) -->
            <?php if($detail['price_promotion'] > 0) { ?>
                <p>Giá KM: <?php echo number_format($detail['price_promotion']);?> đ</p>
            <?php } ?>
        <?php } ?>
    </div>
<?php }?>

4. Lấy danh mục sản phẩm tin tức

Code để lấy danh mục theo điều kiện:

Các thuộc tính của danh mục
Thuộc tính Kiểu dữ liệu Mô tả
idint(11)ID của danh mục
namevarchar(255)Tên danh mục
slugvarchar(255)Đường dẫn URL của danh mục. Sử dụng: <?php echo '/' . $val['slug'];?>
parent_idint(11)ID danh mục cha (0 nếu là danh mục gốc)
iconvarchar(255)Icon của danh mục (tên class icon hoặc đường dẫn icon)
imagesvarchar(255)Hình ảnh danh mục (size: 300x300 - width x height). Sử dụng: <?php echo $this->getImageUrl($val['images'], '300x300');?> (không ghi size sẽ lấy hình gốc)
descriptiontextMô tả ngắn của danh mục
meta_titlevarchar(255)Tiêu đề SEO (dùng cho thẻ <title>)
meta_descriptionvarchar(300)Mô tả SEO (dùng cho thẻ meta description)
meta_keywordvarchar(300)Từ khóa SEO (dùng cho thẻ meta keywords)
fb_titlevarchar(255)Tiêu đề Facebook (dùng cho Open Graph)
fb_descriptionvarchar(300)Mô tả Facebook (dùng cho Open Graph)
fb_imagevarchar(255)Hình ảnh Facebook (size: 300x300 - width x height). Sử dụng: <?php echo $this->getImageUrl($val['fb_image'], '300x300');?> (không ghi size sẽ lấy hình gốc)
detailtextChi tiết đầy đủ của danh mục
user_insertint(11)ID người tạo
user_updateint(11)ID người cập nhật
statustinyint(1)Trạng thái (0 = ẩn, 1 = hiển thị)
created_attimestampThời gian tạo (định dạng: Y-m-d H:i:s)
updated_attimestampThời gian cập nhật (định dạng: Y-m-d H:i:s)
sortint(11)Thứ tự sắp xếp (số càng nhỏ càng hiển thị trước)
pint(11)Phân loại (dùng để lọc danh mục. Ví dụ: if($val['p'] == 13)). Giá trị p chính là tham số sau URL trong admin danh mục, ví dụ: https://hdwebs.vn/admin/category/index?p=13
checkbox1tinyint(1)Checkbox 1 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox1'] == 1) để lấy danh mục hiển thị ngoài trang chủ)
checkbox2tinyint(1)Checkbox 2 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox2'] == 1))
checkbox3tinyint(1)Checkbox 3 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox3'] == 1))
checkbox4tinyint(1)Checkbox 4 (giá trị 0 hoặc 1, dùng làm điều kiện lọc. Ví dụ: if($val['checkbox4'] == 1))
Lấy danh mục hiển thị ngoài trang chủ (checkbox1 = 1)
<?php 
    // Lấy danh mục có checkbox1 = 1 để hiển thị ngoài trang chủ
    foreach($list_cat as $val) {
        if($val['checkbox1'] == 1) {?>
        <div>
            <h3><?php echo $val['name'];?></h3>
            <img src="<?php echo $this->getImageUrl($val['images'], '300x300');?>" alt="<?php echo $val['name'];?>">
            <p><?php echo $val['description'];?></p>
            <a href="<?php echo '/' . $val['slug'];?>">Xem chi tiết</a>
        </div>
    <?php }
    }
?>
Lấy danh mục kết hợp nhiều điều kiện (p = 13 và checkbox1 = 1)
<?php 
    // Lấy danh mục có p = 13 VÀ checkbox1 = 1
    foreach($list_cat as $val) {
        if($val['p'] == 13 && $val['checkbox1'] == 1) {?>
        <div>
            <h3><?php echo $val['name'];?></h3>
            <!-- Code hiển thị danh mục ở đây -->
        </div>
    <?php }
    }
?>
Ví dụ sử dụng các thuộc tính
<?php foreach($list_cat as $val) {
    if($val['p'] == 13) {?>
    <div>
        <h3><?php echo $val['name'];?></h3>
        <img src="<?php echo $this->getImageUrl($val['images'], '300x300');?>" alt="<?php echo $val['name'];?>">
        <p><?php echo $val['description'];?></p>
        <a href="<?php echo '/' . $val['slug'];?>">Xem chi tiết</a>
    </div>
<?php }
}?>
Lấy danh mục có p = 13
<?php foreach($list_cat as $val) {
    if($val['p'] == 13) {?>
    // Code hiển thị danh mục ở đây
<?php }
}?>
Lấy chi tiết danh mục
<?php if(!empty($detail)) {?>
    <div>
        <h1><?php echo $detail['name'];?></h1>
        <img src="<?php echo $this->getImageUrl($detail['images'], '800x600');?>" alt="<?php echo $detail['name'];?>">
        <p><?php echo $detail['description'];?></p>
        <div><?php echo $detail['detail'];?></div>
        
        <!-- Icon danh mục -->
        <?php if(!empty($detail['icon'])) { ?>
            <i class="<?php echo $detail['icon'];?>"></i>
        <?php } ?>
    </div>
<?php }?>

5. Ghép code các block

Code để lấy và hiển thị các block:

Các thuộc tính của block
Thuộc tính Kiểu dữ liệu Mô tả
idint(11)ID của block
namevarchar(255)Tên block
user_insertint(11)ID người tạo
user_updateint(11)ID người cập nhật
statustinyint(1)Trạng thái (0 = ẩn, 1 = hiển thị)
created_attimestampThời gian tạo (định dạng: Y-m-d H:i:s)
updated_attimestampThời gian cập nhật (định dạng: Y-m-d H:i:s)
sortint(11)Thứ tự sắp xếp (số càng nhỏ càng hiển thị trước)
id_pagesint(11)ID trang chứa block
datatextDữ liệu block dạng text (đã được decode JSON thành array). Chỉ cần sử dụng: <?php echo $blocks_55['tên_thuộc_tính'];?> (tên_thuộc_tính chính là tên không dấu)
id_blocksint(11)ID loại block. Tên biến sử dụng: $blocks_55, $blocks_56, $blocks_57 (số 55, 56, 57 chính là giá trị id_blocks). Lưu ý: Tên biến này có thể copy trong admin
imagesvarchar(255)Hình ảnh block (chỉ lấy ra trong admin, không sử dụng ngoài frontend)
Lấy danh sách block
<?php foreach ($blocks_55['list'] as $key => $val){?>
    // Code hiển thị block ở đây
<?php }?>
Các ví dụ sử dụng block
Giải thích:
  • $blocks_55, $blocks_56, $blocks_57: Tên biến block (số 55, 56, 57 là ID loại block - id_blocks). Lưu ý: Tên biến này có thể copy trong admin
  • Block có 2 kiểu:
    • ['list']: Kiểu danh sách - Duyệt qua nhiều item trong block (ví dụ: $blocks_55['list']). Sử dụng vòng lặp foreach để duyệt qua từng item
    • ['detail']: Kiểu chi tiết - Chỉ có 1 item (ví dụ: $blocks_56['detail']). Lấy trực tiếp không cần vòng lặp
Ví dụ kiểu list (danh sách):
<?php foreach ($blocks_55['list'] as $key => $val){?>
    <!-- Tên hình -->
    <?php echo $val['multi_images'][0]['name'][0]; ?>
    
    <!-- Lấy hình (size: 1600x420) -->
    <img src="<?php echo $this->getImageUrl($val['multi_images'][0]['imagesPC'], '1600x420'); ?>" alt="">
    
    <!-- Text items -->
    <?php echo $val['text'][0];?>
<?php }?>
Ví dụ kiểu detail (chi tiết):
<?php 
    // Chi tiết items (block detail)
    echo $blocks_56['detail']['detail'];
?>

<?php 
    // Hình ảnh từ block detail (size: 560x430)
    echo $this->getImageUrl($blocks_56['detail']['multi_images'][0]['imagesPC'], '560x430');
?>

<?php 
    // Block detail text
    echo $blocks_57['detail']['text'][0];
?>

6. Dữ liệu contact form

Code để lấy và hiển thị dữ liệu contact form:

Các thuộc tính của contact form
Thuộc tính Kiểu dữ liệu Mô tả
idint(11)ID của contact form
namevarchar(100)Tên contact form
datatextDữ liệu bổ sung dạng text (đã được decode JSON thành array). Sử dụng: <?php echo $val['data']['tên sản phẩm'];?> (tên sản phẩm là tên trường động ngoài các column trong bảng, đặt tên theo định dạng data['tên trường'])
fullnamevarchar(255)Họ và tên người gửi
emailvarchar(255)Email người gửi
phonevarchar(255)Số điện thoại
tieu_devarchar(255)Tiêu đề
dia_chivarchar(255)Địa chỉ
noi_dungtextNội dung tin nhắn
pint(11)Phân loại (dùng để lọc contact form. Ví dụ: if($val['p'] == 3)). Giá trị p chính là tham số sau URL trong admin
statusint(11)Trạng thái (0 = chưa đọc, 1 = đã đọc)
notestextGhi chú
user_updateint(11)ID người cập nhật
count_requestint(11)Số lần yêu cầu
created_attimestampThời gian gửi (định dạng: Y-m-d H:i:s)
updated_attimestampThời gian cập nhật (định dạng: Y-m-d H:i:s)
Lưu ý:

Ngoài các thuộc tính cố định: fullname, email, phone, tieu_de, dia_chi, noi_dung, các thuộc tính khác sẽ được đặt tên theo định dạng data['tên text'] (ví dụ: data['tên sản phẩm'], data['số lượng']). Các trường này sẽ được lưu vào cột data dưới dạng JSON.

Code form contact
<form id="contactForm" method="POST">
    <input type="hidden" name="p" value="3">
    
    <div class="form-group">
        <label for="fullname">Họ và tên *</label>
        <input type="text" id="fullname" name="fullname" required placeholder="Nhập họ và tên của bạn">
    </div>
    
    <div class="form-group">
        <label for="phone">Số điện thoại *</label>
        <input type="tel" id="phone" name="phone" required placeholder="Nhập số điện thoại">
    </div>
    
    <div class="form-group">
        <label for="email">Email *</label>
        <input type="email" id="email" name="email" required placeholder="Nhập email của bạn">
    </div>
    
    <div class="form-group">
        <label for="tieu_de">Dịch vụ</label>
        <select id="tieu_de" name="tieu_de">
            <option value="">-- Chọn dịch vụ --</option>
            <?php if (!empty($data['list_items']) && is_array($data['list_items'])): ?>
                <?php foreach ($data['list_items'] as $item): ?>
                    <?php if (isset($item['p']) && $item['p'] == 23): ?>
                        <option value="<?php echo !empty($item['name']) ? htmlspecialchars($item['name']) : ''; ?>">
                            <?php echo !empty($item['name']) ? htmlspecialchars($item['name']) : ''; ?>
                        </option>
                    <?php endif; ?>
                <?php endforeach; ?>
            <?php endif; ?>
        </select>
    </div>
    
    <div class="form-group">
        <label for="dia_chi">Địa chỉ</label>
        <input type="text" id="dia_chi" name="dia_chi" placeholder="Nhập địa chỉ">
    </div>
    
    <div class="form-group">
        <label for="noi_dung">Nội dung tin nhắn *</label>
        <textarea id="noi_dung" name="noi_dung" required placeholder="Nhập nội dung tin nhắn của bạn"></textarea>
    </div>
    
    <!-- Các trường động ngoài column, sử dụng data['tên trường'] -->
    <div class="form-group">
        <label for="ten_san_pham">Tên sản phẩm</label>
        <input type="text" id="ten_san_pham" name="data[tên sản phẩm]" placeholder="Nhập tên sản phẩm">
    </div>
    
    <button type="submit" class="btn-submit">Gửi tin nhắn</button>
</form>
Code JavaScript gửi form
<script>
// Hàm gửi contact form
function handleSendContact(formId) {
    const contactForm = document.getElementById(formId || 'contactForm');
    
    if (!contactForm) {
        alert('Không tìm thấy form!');
        return;
    }
    
    // Get form data
    const formData = new FormData(contactForm);
    const fullname = formData.get('fullname');
    const phone = formData.get('phone');
    const email = formData.get('email');
    const noi_dung = formData.get('noi_dung');
    
    // Basic validation
    if (!fullname || !phone || !email || !noi_dung) {
        alert('Vui lòng điền đầy đủ các trường bắt buộc (*)');
        return;
    }
    
    // Email validation
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
        alert('Vui lòng nhập email hợp lệ');
        return;
    }
    
    // Phone validation (Vietnamese phone number)
    const phoneRegex = /^(0|\+84)[0-9]{9,10}$/;
    if (!phoneRegex.test(phone.replace(/\s/g, ''))) {
        alert('Vui lòng nhập số điện thoại hợp lệ');
        return;
    }
    
    // Disable submit button
    const submitBtn = contactForm.querySelector('.btn-submit');
    const originalText = submitBtn ? submitBtn.textContent : '';
    if (submitBtn) {
        submitBtn.disabled = true;
        submitBtn.textContent = 'Đang gửi...';
    }
    
    // Send form data via AJAX
    fetch('/frontend/ajaxs/handleSendContact', {
        method: 'POST',
        body: formData,
        headers: {
            'X-Requested-With': 'XMLHttpRequest'
        }
    })
    .then(res => {
        if (!res.ok) {
            throw new Error(`HTTP error! status: ${res.status}`);
        }
        return res.json();
    })
    .then(response => {
        if (response.success) {
            alert(response.message || 'Cảm ơn bạn đã liên hệ! Chúng tôi sẽ phản hồi sớm nhất có thể.');
            contactForm.reset();
        } else {
            alert(response.message || 'Có lỗi xảy ra khi gửi liên hệ. Vui lòng thử lại sau.');
        }
    })
    .catch(error => {
        console.error('Error:', error);
        alert('Có lỗi xảy ra khi gửi liên hệ. Vui lòng thử lại sau.');
    })
    .finally(() => {
        // Restore submit button
        if (submitBtn) {
            submitBtn.textContent = originalText;
            submitBtn.disabled = false;
        }
    });
}

// Sử dụng với form submit
document.addEventListener("DOMContentLoaded", function() {
    const contactForm = document.getElementById('contactForm');
    
    if (contactForm) {
        contactForm.addEventListener('submit', function(e) {
            e.preventDefault();
            handleSendContact('contactForm');
        });
    }
});
</script>

<!-- Hoặc sử dụng trực tiếp trong button -->
<!-- <button type="button" class="btn-submit" onclick="handleSendContact('contactForm')">Gửi tin nhắn</button> -->

7. Ghép code chi tiết (admin — Xử lý code)

Phần này dành cho team dùng tài khoản code trong admin: vào Code web (hoặc luồng tương đương), chọn website, mở Xử lý code / tạo block cho page, dán HTML mẫu và để hệ thống ghép biến $detail, ảnh, giá, breadcrumb.

Class gốc và biến
  • HTML template thường có class gốc dạng hd-webs-vn-{id} (theo page/block trong admin).
  • Trang chi tiết sản phẩm/tin: dùng $detail (tên, slug, ảnh, giá, category_id, …), $list_cat (danh mục), hằng URL, URL_ASSETS_WEBSITE, và $list_items khi page cần danh sách.
Breadcrumb tự động (chi tiết)

Nếu trong HTML có phần tử mà class hoặc id chứa chữ breadcrumb (không phân biệt hoa thường), khi ghép code chi tiết hệ thống sẽ thay toàn bộ nội dung bên trong bằng đoạn PHP: Trang chủ (link theo URL) → tên danh mục (link: khớp $detail['category_id'] với $list_cat) → tên sản phẩm (link theo $detail['slug']). Giữ nguyên thẻ bọc (ví dụ <nav>, <div>) — chỉ nội dung bên trong được thay.

Gợi ý gallery (ảnh phụ)

Thư viện ảnh nằm ở $detail['gallery_json'] (mảng tên file). Có thể dùng một vòng lặp cho ảnh lớn và một vòng lặp cho dải thumbnail, đồng bộ chỉ số (cùng một mảng nguồn) — phù hợp slider kiểu gallery chính + strip ảnh nhỏ.

Zalo