daemon-idc init...1
This commit is contained in:
parent
4916b7c5bf
commit
d6e1db1155
@ -93,7 +93,7 @@ class App extends BaseConfig
|
||||
* strings (like currency markers, numbers, etc), that your program
|
||||
* should run under for this request.
|
||||
*/
|
||||
public string $defaultLocale = 'en';
|
||||
public string $defaultLocale = 'ko';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
|
||||
@ -288,3 +288,10 @@ define("BOARD", [
|
||||
'REQUESTTASK' => 'requesttask'
|
||||
],
|
||||
]);
|
||||
|
||||
//사이트 선택관련
|
||||
define("SITES", [
|
||||
"primeidc" => "PRIME",
|
||||
"itsolution" => "ITSOLUTION",
|
||||
"gdidc" => "GDIDC",
|
||||
]);
|
||||
107
app/Config/Layout.php
Normal file
107
app/Config/Layout.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Layout extends BaseConfig
|
||||
{
|
||||
public const KEYWORD = '일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버 가상서버';
|
||||
|
||||
public array $layouts = [
|
||||
'auth' => [
|
||||
'title' => self::KEYWORD,
|
||||
'path' => 'auth',
|
||||
'layout' => 'layouts' . DIRECTORY_SEPARATOR . 'auth',
|
||||
'template' => 'templates' . DIRECTORY_SEPARATOR . 'auth',
|
||||
'metas' => [
|
||||
'<meta charset="UTF-8">',
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1.0">',
|
||||
'<meta http-equiv="X-UA-Compatible" content="IE=Edge">',
|
||||
'<meta name="subject" content="Daemon IDC">',
|
||||
'<meta name="description" content="' . self::KEYWORD . '">',
|
||||
'<meta name="keywords" content="' . self::KEYWORD . '">',
|
||||
'<meta property="og:type" content="website">',
|
||||
'<meta property="og:title" content="Daemon IDC">',
|
||||
'<meta property="og:description" content="' . self::KEYWORD . '">',
|
||||
],
|
||||
'stylesheets' => [
|
||||
'<link rel="icon" href="/favicon.ico">',
|
||||
'<link href="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">',
|
||||
'<link rel="stylesheet" href="/css/common/style.css" />',
|
||||
],
|
||||
'javascripts' => [
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>',
|
||||
],
|
||||
'footerScripts' => []
|
||||
],
|
||||
'front' => [
|
||||
'title' => self::KEYWORD,
|
||||
'path' => 'front',
|
||||
'layout' => 'layouts' . DIRECTORY_SEPARATOR . 'front',
|
||||
'template' => 'templates' . DIRECTORY_SEPARATOR . 'front',
|
||||
'topmenus' => ['aboutus', 'hosting', 'service', 'support'],
|
||||
'metas' => [
|
||||
'<meta charset="UTF-8">',
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1.0">',
|
||||
'<meta http-equiv="X-UA-Compatible" content="IE=Edge">',
|
||||
'<meta name="subject" content="Daemon IDC">',
|
||||
'<meta name="description" content="' . self::KEYWORD . '">',
|
||||
'<meta name="keywords" content="' . self::KEYWORD . '">',
|
||||
'<meta property="og:type" content="website">',
|
||||
'<meta property="og:title" content="Daemon IDC">',
|
||||
'<meta property="og:description" content="' . self::KEYWORD . '">',
|
||||
],
|
||||
'stylesheets' => [
|
||||
'<link rel="icon" href="/favicon.ico">',
|
||||
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">',
|
||||
'<link rel="stylesheet" href="/css/common/style.css" />',
|
||||
],
|
||||
'javascripts' => [
|
||||
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
|
||||
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
|
||||
],
|
||||
'footerScripts' => []
|
||||
],
|
||||
'admin' => [
|
||||
'title' => '관리자화면',
|
||||
'path' => 'admin',
|
||||
'layout' => 'layouts' . DIRECTORY_SEPARATOR . 'admin',
|
||||
'template' => 'templates' . DIRECTORY_SEPARATOR . 'admin',
|
||||
'metas' => [
|
||||
'<meta charset="UTF-8">',
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1.0">',
|
||||
'<meta http-equiv="X-UA-Compatible" content="IE=Edge">',
|
||||
'<meta name="subject" content="Daemon IDC">',
|
||||
'<meta name="description" content="' . self::KEYWORD . '">',
|
||||
'<meta name="keywords" content="' . self::KEYWORD . '">',
|
||||
'<meta property="og:type" content="website">',
|
||||
'<meta property="og:title" content="Daemon IDC">',
|
||||
'<meta property="og:description" content="' . self::KEYWORD . '">',
|
||||
],
|
||||
'stylesheets' => [
|
||||
'<link rel="icon" href="/favicon.ico">',
|
||||
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />',
|
||||
'<link rel="stylesheet" href="/assets/tagify/dist/tagify.css">',
|
||||
'<link rel="stylesheet" href="/css/common/style.css" />',
|
||||
],
|
||||
'javascripts' => [
|
||||
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
|
||||
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
|
||||
'<script src="/assets/tinymce/tinymce.min.js" referrerpolicy="origin"></script>',
|
||||
'<script src="/assets/tagify/dist/tagify.js"></script>'
|
||||
],
|
||||
'footerScripts' => []
|
||||
],
|
||||
];
|
||||
}
|
||||
@ -8,7 +8,7 @@ use App\Services\Auth\GoogleService;
|
||||
use App\Services\Auth\LocalService;
|
||||
use App\Services\BoardService;
|
||||
use App\Services\UserService;
|
||||
|
||||
use App\Services\Customer\ClientService;
|
||||
/**
|
||||
* Services Configuration file.
|
||||
*
|
||||
@ -86,4 +86,15 @@ class Services extends BaseService
|
||||
new \App\Models\BoardModel(),
|
||||
);
|
||||
}
|
||||
|
||||
//Customer
|
||||
public static function customer_clientservice($getShared = true): ClientService
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance(__FUNCTION__);
|
||||
}
|
||||
return new ClientService(
|
||||
new \App\Models\Customer\ClientModel(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
70
app/Controllers/Admin/Customer/ClientController.php
Normal file
70
app/Controllers/Admin/Customer/ClientController.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Admin\Customer;
|
||||
|
||||
use App\Entities\Customer\ClientEntity;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class ClientController extends CustomerController
|
||||
{
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
parent::initController($request, $response, $logger);
|
||||
if ($this->service === null) {
|
||||
$this->service = service('customer_clientservice');
|
||||
}
|
||||
$this->addActionPaths('client');
|
||||
}
|
||||
//기본 함수 작업
|
||||
//Custom 추가 함수
|
||||
//고객 상세정보
|
||||
public function detail(mixed $uid): string|RedirectResponse
|
||||
{
|
||||
try {
|
||||
$action = __FUNCTION__;
|
||||
$this->action_init_process($action);
|
||||
//Return Url정의
|
||||
$this->getAuthContext()->pushCurrentUrl($this->request->getUri()->getPath() . ($this->request->getUri()->getQuery() ? "?" . $this->request->getUri()->getQuery() : ""));
|
||||
//일괄작업용 Fields정의
|
||||
$entity = $this->service->getEntity($uid);
|
||||
if (!$entity instanceof ClientEntity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$uid}에 해당하는 고객정보를 찾을수 없습니다.");
|
||||
}
|
||||
$this->addViewDatas('totalCounts', service('equipment_serverservice')->getTotalServiceCount(['serviceinfo.clientinfo_uid' => $entity->getPK()]));
|
||||
$this->addViewDatas('totalAmounts', service('customer_serviceservice')->getTotalAmounts([
|
||||
'clientinfo_uid' => $entity->getPK(),
|
||||
'status' => STATUS['AVAILABLE']
|
||||
]));
|
||||
//서비스별 미납 Count
|
||||
$this->addViewDatas('unPaids', service('paymentservice')->getUnPaids('clientinfo_uid', [
|
||||
'clientinfo_uid' => $entity->getPK()
|
||||
]));
|
||||
$this->addViewDatas('serviceEntities', service('customer_serviceservice')->getEntities(['clientinfo_uid' => $entity->getPK()]));
|
||||
$this->addViewDatas('entity', $entity);
|
||||
helper(['form']);
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate') ?? 'client');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 고객 Detail Page 오류:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//비고사항 변경
|
||||
public function history(int $uid): RedirectResponse|string
|
||||
{
|
||||
try {
|
||||
$history = $this->request->getPost('history');
|
||||
if (!$history) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 비고가 정의되지 않았습니다.");
|
||||
}
|
||||
$entity = $this->service->modify($uid, ['history' => $history]);
|
||||
$this->addViewDatas('entity', $entity);
|
||||
return $this->action_redirect_process('info', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 비고설정이 완료되었습니다.");
|
||||
} catch (\Throwable $e) {
|
||||
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 비고설정 오류:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
19
app/Controllers/Admin/Customer/CustomerController.php
Normal file
19
app/Controllers/Admin/Customer/CustomerController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Admin\Customer;
|
||||
|
||||
use App\Controllers\Admin\AdminController;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
abstract class CustomerController extends AdminController
|
||||
{
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
parent::initController($request, $response, $logger);
|
||||
$this->addActionPaths('customer');
|
||||
}
|
||||
//Index,FieldForm관련
|
||||
}
|
||||
@ -40,7 +40,7 @@ class Home extends AbstractWebController
|
||||
$this->addViewDatas('boardRequestTaskCount', service('boardservice')->getRequestTaskCount($this->getAuthContext()->getUID()));
|
||||
//Total 서버 현황
|
||||
//interval을 기준으로 최근 신규 서비스정보 가져오기
|
||||
$interval = intval($this->request->getVar('interval') ?? SERVICE['NEW_INTERVAL']);
|
||||
$interval = intval($this->request->getVar('interval') ?? 7);
|
||||
$this->addViewDatas('interval', $interval);
|
||||
$newServiceEntities = $this->service->getNewServiceEntities($interval);
|
||||
$this->addViewDatas('newServiceEntities', $newServiceEntities);
|
||||
|
||||
@ -2,10 +2,40 @@
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
class Home extends BaseController
|
||||
use App\Controllers\AbstractWebController;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Home extends AbstractWebController
|
||||
{
|
||||
private $_layout = 'front';
|
||||
protected $layouts = [];
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
parent::initController($request, $response, $logger);
|
||||
if ($this->service === null) {
|
||||
$this->service = service('customer_clientservice');
|
||||
}
|
||||
$this->addActionPaths($this->_layout);
|
||||
$this->layouts = config('Layout')->layouts[$this->_layout] ?? [];
|
||||
}
|
||||
protected function action_init_process(string $action, array $formDatas = []): void
|
||||
{
|
||||
parent::action_init_process($action, $formDatas);
|
||||
$this->addViewDatas('layout', $this->layouts);
|
||||
$this->addViewDatas('helper', $this->service->getHelper());
|
||||
$this->service->getActionForm()->action_init_process($action, $formDatas);
|
||||
$this->addViewDatas('formFields', $this->service->getActionForm()->getFormFields());
|
||||
$this->addViewDatas('formRules', $this->service->getActionForm()->getFormRules());
|
||||
$this->addViewDatas('formFilters', $this->service->getActionForm()->getFormFilters());
|
||||
$this->addViewDatas('formOptions', $this->service->getActionForm()->getFormOptions());
|
||||
}
|
||||
//Index,FieldForm관련
|
||||
public function index(): string
|
||||
{
|
||||
return view('welcome_message');
|
||||
$action = __FUNCTION__;
|
||||
$this->action_init_process($action);
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate') ?? "welcome");
|
||||
}
|
||||
}
|
||||
|
||||
51
app/DTOs/Customer/ClientDTO.php
Normal file
51
app/DTOs/Customer/ClientDTO.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs\Customer;
|
||||
|
||||
use App\DTOs\CommonDTO;
|
||||
|
||||
class ClientDTO extends CommonDTO
|
||||
{
|
||||
public ?int $uid = null;
|
||||
public ?int $user_uid = null;
|
||||
public ?string $id = null;
|
||||
public ?string $passwd = null;
|
||||
public string $site = '';
|
||||
public string $name = '';
|
||||
public string $phone = '';
|
||||
public string $email = '';
|
||||
public array $role = [];
|
||||
public int $account_balance = 0;
|
||||
public int $coupon_balance = 0;
|
||||
public int $point_balance = 0;
|
||||
public string $status = '';
|
||||
public string $history = '';
|
||||
|
||||
public function __construct(array $datas = [])
|
||||
{
|
||||
// 1. role 변환 로직 (기존 유지)
|
||||
if (isset($datas['role']) && is_string($datas['role'])) {
|
||||
$datas['role'] = explode(DEFAULTS["DELIMITER_COMMA"], $datas['role']);
|
||||
}
|
||||
|
||||
if (!isset($datas['role'])) {
|
||||
$datas['role'] = [];
|
||||
}
|
||||
|
||||
// 2. [추가] 잔액 관련 데이터가 null이거나 비어있다면 0으로 보정
|
||||
// CommonDTO의 parent::__construct가 호출되기 전에 데이터를 정제합니다.
|
||||
$balanceFields = ['account_balance', 'coupon_balance', 'point_balance'];
|
||||
foreach ($balanceFields as $field) {
|
||||
if (!isset($datas[$field]) || $datas[$field] === '' || $datas[$field] === null) {
|
||||
$datas[$field] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($datas);
|
||||
}
|
||||
|
||||
public function getRoleToString(): string
|
||||
{
|
||||
return implode(DEFAULTS["DELIMITER_COMMA"], $this->role);
|
||||
}
|
||||
}
|
||||
@ -83,6 +83,43 @@ INSERT INTO `user` VALUES (1,'choi.jh','$2y$10$.vl2FtwJsjMNFCJJm3ISDu7m3vBB85mZ5
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
--
|
||||
-- Table structure for table `clientinfo`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `clientinfo`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `clientinfo` (
|
||||
`uid` int(11) NOT NULL AUTO_INCREMENT COMMENT '고객정보',
|
||||
`user_uid` int(11) NOT NULL COMMENT '관리자정보',
|
||||
`id` varchar(20) DEFAULT NULL,
|
||||
`passwd` varchar(255) DEFAULT NULL,
|
||||
`site` varchar(20) NOT NULL DEFAULT 'prime' COMMENT 'Site구분',
|
||||
`role` varchar(50) NOT NULL DEFAULT 'user',
|
||||
`name` varchar(100) NOT NULL,
|
||||
`phone` varchar(50) DEFAULT NULL,
|
||||
`email` varchar(50) NOT NULL,
|
||||
`history` text DEFAULT NULL COMMENT 'history',
|
||||
`account_balance` int(11) NOT NULL DEFAULT 0 COMMENT '예치금',
|
||||
`coupon_balance` int(11) NOT NULL DEFAULT 0 COMMENT '쿠폰수',
|
||||
`point_balance` int(11) NOT NULL DEFAULT 0 COMMENT '포인트',
|
||||
`status` varchar(20) NOT NULL DEFAULT 'available',
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
`deleted_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`uid`),
|
||||
UNIQUE KEY `unique_site_name` (`site`,`name`),
|
||||
UNIQUE KEY `uk_id` (`id`),
|
||||
KEY `FK_user_TO_clientinfo` (`user_uid`),
|
||||
CONSTRAINT `FK_user_TO_clientinfo` FOREIGN KEY (`user_uid`) REFERENCES `user` (`uid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='고객정보';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `clientinfo`
|
||||
--
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
@ -92,3 +129,4 @@ UNLOCK TABLES;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2026-02-09 17:04:26
|
||||
|
||||
|
||||
142
app/Entities/Customer/ClientEntity.php
Normal file
142
app/Entities/Customer/ClientEntity.php
Normal file
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entities\Customer;
|
||||
|
||||
use App\Models\Customer\ClientModel;
|
||||
|
||||
class ClientEntity extends CustomerEntity
|
||||
{
|
||||
const PK = ClientModel::PK;
|
||||
const TITLE = ClientModel::TITLE;
|
||||
|
||||
protected array $nullableFields = [
|
||||
'id',
|
||||
'passwd',
|
||||
];
|
||||
|
||||
// ✅ role은 반드시 string 기본값
|
||||
protected $attributes = [
|
||||
'id' => null,
|
||||
'passwd' => null,
|
||||
'site' => '',
|
||||
'name' => '',
|
||||
'phone' => '',
|
||||
'email' => '',
|
||||
'role' => '', // ✅ [] 금지
|
||||
'account_balance' => 0,
|
||||
'coupon_balance' => 0,
|
||||
'point_balance' => 0,
|
||||
'status' => '',
|
||||
'history' => '',
|
||||
];
|
||||
|
||||
public function __construct(array|null $data = null)
|
||||
{
|
||||
parent::__construct($data);
|
||||
}
|
||||
|
||||
public function getUserUid(): int|null
|
||||
{
|
||||
return $this->user_uid ?? null;
|
||||
}
|
||||
|
||||
public function getCustomTitle(mixed $title = null): string
|
||||
{
|
||||
return sprintf("%s/%s", $this->getSite(), $title ? $title : $this->getTitle());
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return (string) ($this->attributes['name'] ?? '');
|
||||
}
|
||||
|
||||
public function getSite(): string
|
||||
{
|
||||
return (string) ($this->attributes['site'] ?? '');
|
||||
}
|
||||
|
||||
public function getAccountBalance(): int
|
||||
{
|
||||
return (int) ($this->attributes['account_balance'] ?? 0);
|
||||
}
|
||||
|
||||
public function getCouponBalance(): int
|
||||
{
|
||||
return (int) ($this->attributes['coupon_balance'] ?? 0);
|
||||
}
|
||||
|
||||
public function getPointBalance(): int
|
||||
{
|
||||
return (int) ($this->attributes['point_balance'] ?? 0);
|
||||
}
|
||||
|
||||
public function getHistory(): string|null
|
||||
{
|
||||
return $this->attributes['history'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* role을 배열로 반환
|
||||
*/
|
||||
public function getRole(): array
|
||||
{
|
||||
$role = $this->attributes['role'] ?? null;
|
||||
|
||||
if (is_array($role)) {
|
||||
return array_values(array_filter($role, fn($v) => (string) $v !== ''));
|
||||
}
|
||||
|
||||
if (is_string($role) && $role !== '') {
|
||||
$decoded = json_decode($role, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
$clean = array_map(
|
||||
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
|
||||
$decoded
|
||||
);
|
||||
return array_values(array_filter($clean, fn($v) => $v !== ''));
|
||||
}
|
||||
|
||||
$parts = explode(DEFAULTS["DELIMITER_COMMA"], $role);
|
||||
$clean = array_map(
|
||||
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
|
||||
$parts
|
||||
);
|
||||
return array_values(array_filter($clean, fn($v) => $v !== ''));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ role은 DB 저장용 CSV 문자열로 반환
|
||||
*/
|
||||
public function setRole($role): string
|
||||
{
|
||||
$roleArray = [];
|
||||
|
||||
if (is_string($role)) {
|
||||
$clean = trim($role, " \t\n\r\0\x0B\"");
|
||||
if ($clean !== '') {
|
||||
$decoded = json_decode($clean, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
$roleArray = $decoded;
|
||||
} else {
|
||||
$roleArray = explode(DEFAULTS["DELIMITER_COMMA"], $clean);
|
||||
}
|
||||
}
|
||||
} elseif (is_array($role)) {
|
||||
$roleArray = $role;
|
||||
} else {
|
||||
$roleArray = [];
|
||||
}
|
||||
|
||||
$cleaned = array_map(
|
||||
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
|
||||
$roleArray
|
||||
);
|
||||
|
||||
$roleArray = array_values(array_filter($cleaned, fn($v) => $v !== ''));
|
||||
|
||||
return implode(DEFAULTS["DELIMITER_COMMA"], $roleArray);
|
||||
}
|
||||
}
|
||||
13
app/Entities/Customer/CustomerEntity.php
Normal file
13
app/Entities/Customer/CustomerEntity.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entities\Customer;
|
||||
|
||||
use App\Entities\CommonEntity;
|
||||
|
||||
abstract class CustomerEntity extends CommonEntity
|
||||
{
|
||||
public function __construct(array|null $data = null)
|
||||
{
|
||||
parent::__construct($data);
|
||||
}
|
||||
}
|
||||
87
app/Forms/Customer/ClientForm.php
Normal file
87
app/Forms/Customer/ClientForm.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Forms\Customer;
|
||||
|
||||
class ClientForm extends CustomerForm
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
public function action_init_process(string $action, array &$formDatas = []): void
|
||||
{
|
||||
$fields = [
|
||||
'site',
|
||||
'name',
|
||||
'email',
|
||||
'phone',
|
||||
'role',
|
||||
'status',
|
||||
];
|
||||
$filters = [
|
||||
'site',
|
||||
'role',
|
||||
'status',
|
||||
];
|
||||
$indexFilter = $filters;
|
||||
$batchjobFilters = ['site', 'role', 'status'];
|
||||
switch ($action) {
|
||||
case 'view':
|
||||
$fields = [...$fields, 'status', 'created_at'];
|
||||
break;
|
||||
case 'index':
|
||||
case 'download':
|
||||
$fields = [
|
||||
'site',
|
||||
'name',
|
||||
'email',
|
||||
'phone',
|
||||
'role',
|
||||
'account_balance',
|
||||
'coupon_balance',
|
||||
'point_balance',
|
||||
'status',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
break;
|
||||
}
|
||||
$this->setFormFields($fields);
|
||||
$this->setFormRules($action, $fields);
|
||||
$this->setFormFilters($filters);
|
||||
$this->setFormOptions($action, $filters, $formDatas);
|
||||
$this->setIndexFilters($indexFilter);
|
||||
$this->setBatchjobFilters($batchjobFilters);
|
||||
}
|
||||
public function getFormRule(string $action, string $field, array $formRules): array
|
||||
{
|
||||
switch ($field) {
|
||||
case "name":
|
||||
$formRules[$field] = sprintf("required|trim|string%s", in_array($action, ["create", "create_form"]) ? "|is_unique[{$this->getAttribute('table')}.{$field}]" : "");
|
||||
break;
|
||||
case "site":
|
||||
$formRules[$field] = "required|trim|string";
|
||||
break;
|
||||
case "role":
|
||||
$formRules[$field] = 'required|is_array|at_least_one';
|
||||
$formRules['role.*'] = 'permit_empty|trim|in_list[user,vip,reseller]';
|
||||
break;
|
||||
case "email":
|
||||
$formRules[$field] = "permit_empty|trim|valid_email";
|
||||
break;
|
||||
case "phone":
|
||||
case "history":
|
||||
$formRules[$field] = "permit_empty|trim|string";
|
||||
break;
|
||||
case "account_balance":
|
||||
case "coupon_balance":
|
||||
case "point_balance":
|
||||
$formRules[$field] = "permit_empty|numeric";
|
||||
break;
|
||||
default:
|
||||
$formRules = parent::getFormRule($action, $field, $formRules);
|
||||
break;
|
||||
}
|
||||
return $formRules;
|
||||
}
|
||||
}
|
||||
13
app/Forms/Customer/CustomerForm.php
Normal file
13
app/Forms/Customer/CustomerForm.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Forms\Customer;
|
||||
|
||||
use App\Forms\CommonForm;
|
||||
|
||||
abstract class CustomerForm extends CommonForm
|
||||
{
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
202
app/Helpers/Customer/ClientHelper.php
Normal file
202
app/Helpers/Customer/ClientHelper.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers\Customer;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class ClientHelper extends CustomerHelper
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
public function getFieldForm(string $field, mixed $value, array $viewDatas, array $extras = []): string
|
||||
{
|
||||
switch ($field) {
|
||||
case 'site':
|
||||
$forms = [];
|
||||
array_shift($viewDatas['formOptions'][$field]['options']);
|
||||
foreach ($viewDatas['formOptions'][$field]['options'] as $key => $label)
|
||||
$forms[] = form_radio($field, $key, $key == $value, $extras) . $label;
|
||||
$form = implode(" ", $forms);
|
||||
break;
|
||||
case 'role':
|
||||
// 1) value가 배열이면 그대로, 문자열이면 CSV를 배열로 변환
|
||||
if (is_string($value)) {
|
||||
$value = trim($value, " \t\n\r\0\x0B\"");
|
||||
$value = ($value === '') ? [] : explode(DEFAULTS["DELIMITER_COMMA"], $value);
|
||||
} elseif (!is_array($value)) {
|
||||
$value = [];
|
||||
}
|
||||
|
||||
// 2) 정리
|
||||
$currentRoles = array_values(array_filter(
|
||||
array_map(
|
||||
fn($item) => strtolower(trim((string) ($item ?? ''), " \t\n\r\0\x0B\"")),
|
||||
$value
|
||||
)
|
||||
));
|
||||
|
||||
$form = '';
|
||||
array_shift($viewDatas['formOptions'][$field]['options']);
|
||||
foreach ($viewDatas['formOptions'][$field]['options'] as $key => $label) {
|
||||
$checked = in_array(strtolower(trim((string) $key)), $currentRoles, true);
|
||||
$form .= '<label class="me-3">';
|
||||
$form .= form_checkbox('role[]', $key, $checked, ['id' => "role_{$key}", ...$extras]);
|
||||
$form .= " {$label}";
|
||||
$form .= '</label>';
|
||||
}
|
||||
// dd($form);
|
||||
break;
|
||||
default:
|
||||
$form = parent::getFieldForm($field, $value, $viewDatas, $extras);
|
||||
break;
|
||||
}
|
||||
return $form;
|
||||
} //
|
||||
public function getFieldView(string $field, mixed $value, array $viewDatas, array $extras = []): string|null
|
||||
{
|
||||
switch ($field) {
|
||||
case 'name':
|
||||
$value = "<a href=\"/admin/customer/client/detail/{$viewDatas['entity']->getPK()}\">" . $value . "</a>";
|
||||
break;
|
||||
case "email":
|
||||
case "phone":
|
||||
//역활이 보안관리자가 아니면 정보숨김
|
||||
$value = $this->getAuthContext()->isAccessRole([ROLE['USER']['SECURITY']]) ? parent::getFieldView($field, $value, $viewDatas, $extras) : "***********";
|
||||
break;
|
||||
case 'account_balance':
|
||||
$value = form_label(
|
||||
number_format($value) . "원",
|
||||
$field,
|
||||
[
|
||||
"data-src" => "/admin/customer/wallet/account?clientinfo_uid={$viewDatas['entity']->getPK()}&ActionTemplate=popup",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "text-primary",
|
||||
...$extras,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'coupon_balance':
|
||||
$value = form_label(
|
||||
number_format($value) . "개",
|
||||
$field,
|
||||
[
|
||||
"data-src" => "/admin/customer/wallet/coupon?clientinfo_uid={$viewDatas['entity']->getPK()}&ActionTemplate=popup",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "text-primary",
|
||||
...$extras,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'point_balance':
|
||||
$value = form_label(
|
||||
number_format($value) . "점",
|
||||
$field,
|
||||
[
|
||||
"data-src" => "/admin/customer/wallet/point?clientinfo_uid={$viewDatas['entity']->getPK()}&ActionTemplate=popup",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "text-primary",
|
||||
...$extras,
|
||||
]
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$value = parent::getFieldView($field, $value, $viewDatas, $extras);
|
||||
break;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
throw new RuntimeException(static::class . "->" . __FUNCTION__ . "에서 오류발생:{$field}에 해당하는 Return 값이 배열형식입니다.\n" . var_export($value, true));
|
||||
}
|
||||
return $value;
|
||||
} //
|
||||
public function getListButton(string $action, string $label, array $viewDatas, array $extras = []): string
|
||||
{
|
||||
switch ($action) {
|
||||
case 'delete':
|
||||
case 'batchjob':
|
||||
case 'batchjob_delete':
|
||||
//역활이 보안관리자가 아니면 사용불가
|
||||
$action = $this->getAuthContext()->isAccessRole([ROLE['USER']['SECURITY']]) ? parent::getListButton($action, $label, $viewDatas, $extras) : "";
|
||||
break;
|
||||
case 'modify':
|
||||
//역활이 보안관리자가 아니면 수정불가
|
||||
$action = $this->getAuthContext()->isAccessRole([ROLE['USER']['SECURITY']]) ? parent::getListButton($action, $label, $viewDatas, $extras) : $label;
|
||||
break;
|
||||
case 'history':
|
||||
$action = form_label(
|
||||
$label ? $label : ICONS['HISTORY'],
|
||||
$action,
|
||||
[
|
||||
"data-src" => "/admin/customer/client/history?clientinfo_uid={$viewDatas['entity']->getPK()}",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "btn btn-sm btn-primary form-label-sm",
|
||||
...$extras
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'coupon':
|
||||
case 'account':
|
||||
case 'point':
|
||||
$action = form_label(
|
||||
$label,
|
||||
$action,
|
||||
[
|
||||
"data-src" => "/admin/customer/wallet/{$action}?clientinfo_uid={$viewDatas['entity']->getPK()}&ActionTemplate=popup",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "text-primary",
|
||||
...$extras
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'invoice':
|
||||
$action = form_label(
|
||||
$label,
|
||||
'payment_invoice',
|
||||
[
|
||||
"data-src" => "/admin/payment?clientinfo_uid={$viewDatas['entity']->getPK()}&ActionTemplate=popup",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "text-primary form-label-sm",
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'addService':
|
||||
$action = form_label(
|
||||
'서비스추가',
|
||||
'create_service',
|
||||
[
|
||||
"data-src" => "/admin/customer/service/create?clientinfo_uid={$viewDatas['entity']->getPK()}",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "btn btn-sm btn-primary form-label-sm",
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'unpaid':
|
||||
$action = "{$label} 0원";
|
||||
if (array_key_exists($viewDatas['entity']->getPK(), $viewDatas['unPaids'])) {
|
||||
$action = form_label(
|
||||
sprintf("%s건/%s원", $viewDatas['unPaids'][$viewDatas['entity']->getPK()]['cnt'], number_format($viewDatas['unPaids'][$viewDatas['entity']->getPK()]['amount'])),
|
||||
'payment_unpaid',
|
||||
[
|
||||
"data-src" => "/admin/payment?clientinfo_uid={$viewDatas['entity']->getPK()}&status=unpaid&ActionTemplate=popup",
|
||||
"data-bs-toggle" => "modal",
|
||||
"data-bs-target" => "#modal_action_form",
|
||||
"class" => "text-primary form-label-sm",
|
||||
]
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$action = parent::getListButton($action, $label, $viewDatas, $extras);
|
||||
break;
|
||||
}
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
13
app/Helpers/Customer/CustomerHelper.php
Normal file
13
app/Helpers/Customer/CustomerHelper.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers\Customer;
|
||||
|
||||
use App\Helpers\CommonHelper;
|
||||
|
||||
abstract class CustomerHelper extends CommonHelper
|
||||
{
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
31
app/Language/ko/Customer/Client.php
Normal file
31
app/Language/ko/Customer/Client.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
return [
|
||||
'title' => "고객정보",
|
||||
'label' => [
|
||||
'user_uid' => "관리자UID",
|
||||
'code' => "고객코드",
|
||||
'site' => "사이트",
|
||||
'email' => "메일",
|
||||
'phone' => "연락처",
|
||||
'role' => "권한",
|
||||
'name' => "이름",
|
||||
'account_balance' => "예치금",
|
||||
'coupon_balance' => "쿠폰",
|
||||
'point_balance' => "포인트",
|
||||
'status' => "상태",
|
||||
'updated_at' => "갱신일",
|
||||
'created_at' => "등록일",
|
||||
'deleted_at' => "삭제일",
|
||||
],
|
||||
"SITE" => SITES,
|
||||
"ROLE" => [
|
||||
ROLE['CLIENT']['USER'] => "일반회원",
|
||||
ROLE['CLIENT']['VIP'] => "VIP회원",
|
||||
ROLE['CLIENT']['RESELLER'] => "리셀러",
|
||||
],
|
||||
"STATUS" => [
|
||||
STATUS['AVAILABLE'] => "사용중",
|
||||
STATUS['PAUSE'] => "일시정지",
|
||||
STATUS['TERMINATED'] => "해지",
|
||||
],
|
||||
];
|
||||
38
app/Models/Customer/ClientModel.php
Normal file
38
app/Models/Customer/ClientModel.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Customer;
|
||||
|
||||
use App\Entities\Customer\ClientEntity;
|
||||
|
||||
class ClientModel extends CustomerModel
|
||||
{
|
||||
const TABLE = "clientinfo";
|
||||
const PK = "uid";
|
||||
const TITLE = "name";
|
||||
protected $table = self::TABLE;
|
||||
// protected $useAutoIncrement = false;
|
||||
protected $primaryKey = self::PK;
|
||||
protected $returnType = ClientEntity::class;
|
||||
protected array $nullableFields = [
|
||||
'id',
|
||||
'passwd',
|
||||
];
|
||||
protected $allowedFields = [
|
||||
"uid",
|
||||
"user_uid",
|
||||
"site",
|
||||
"role",
|
||||
"name",
|
||||
"phone",
|
||||
"email",
|
||||
"history",
|
||||
"account_balance",
|
||||
"coupon_balance",
|
||||
"point_balance",
|
||||
"status",
|
||||
];
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
16
app/Models/Customer/CustomerModel.php
Normal file
16
app/Models/Customer/CustomerModel.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Customer;
|
||||
|
||||
use App\Models\CommonModel;
|
||||
|
||||
abstract class CustomerModel extends CommonModel
|
||||
{
|
||||
//true이면 모든 delete * 메소드 호출은 실제로 행을 삭제하는 것이 아니라 플래그를 데이터베이스로 설정
|
||||
protected $useSoftDeletes = true;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
72
app/Services/Customer/ClientService.php
Normal file
72
app/Services/Customer/ClientService.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Customer;
|
||||
|
||||
use App\DTOs\Customer\ClientDTO;
|
||||
use App\Entities\Customer\ClientEntity;
|
||||
use App\Entities\PaymentEntity;
|
||||
use App\Forms\Customer\ClientForm;
|
||||
use App\Helpers\Customer\ClientHelper;
|
||||
use App\Models\Customer\ClientModel;
|
||||
use RuntimeException;
|
||||
|
||||
class ClientService extends CustomerService
|
||||
{
|
||||
protected string $formClass = ClientForm::class;
|
||||
protected string $helperClass = ClientHelper::class;
|
||||
|
||||
public function __construct(ClientModel $model)
|
||||
{
|
||||
parent::__construct($model);
|
||||
$this->addClassPaths('Client');
|
||||
}
|
||||
public function getDTOClass(): string
|
||||
{
|
||||
return ClientDTO::class;
|
||||
}
|
||||
public function createDTO(array $formDatas): ClientDTO
|
||||
{
|
||||
return new ClientDTO($formDatas);
|
||||
}
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return ClientEntity::class;
|
||||
}
|
||||
//기본 기능부분
|
||||
protected function getEntity_process(mixed $entity): ClientEntity
|
||||
{
|
||||
return $entity;
|
||||
}
|
||||
//List 검색용
|
||||
//FormFilter 조건절 처리
|
||||
//검색어조건절처리
|
||||
//OrderBy 처리
|
||||
public function setOrderBy(mixed $field = null, mixed $value = null): void
|
||||
{
|
||||
$this->model->orderBy("site ASC,name ASC");
|
||||
parent::setOrderBy($field, $value);
|
||||
}
|
||||
|
||||
protected function action_process_fieldhook(string $field, $value, array $formDatas): array
|
||||
{
|
||||
switch ($field) {
|
||||
case 'role':
|
||||
if (is_string($value)) {
|
||||
$value = ($value === '') ? [] : explode(DEFAULTS["DELIMITER_COMMA"], $value);
|
||||
} elseif (!is_array($value)) {
|
||||
$value = [];
|
||||
}
|
||||
$value = array_values(array_filter(array_map(
|
||||
fn($v) => trim((string) ($v ?? ''), " \t\n\r\0\x0B\""),
|
||||
$value
|
||||
)));
|
||||
$formDatas[$field] = $value;
|
||||
break;
|
||||
default:
|
||||
$formDatas = parent::action_process_fieldhook($field, $value, $formDatas);
|
||||
break;
|
||||
}
|
||||
return $formDatas;
|
||||
}
|
||||
|
||||
}
|
||||
15
app/Services/Customer/CustomerService.php
Normal file
15
app/Services/Customer/CustomerService.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Customer;
|
||||
|
||||
use App\Models\CommonModel;
|
||||
use App\Services\CommonService;
|
||||
|
||||
abstract class CustomerService extends CommonService
|
||||
{
|
||||
protected function __construct(CommonModel $model)
|
||||
{
|
||||
parent::__construct($model);
|
||||
$this->addClassPaths('Customer');
|
||||
}
|
||||
}
|
||||
9
app/Views/front/welcome/index.php
Normal file
9
app/Views/front/welcome/index.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?= $this->extend($viewDatas['layout']['layout']) ?>
|
||||
<?= $this->section('content') ?>
|
||||
<!-- Layout Middle Start -->
|
||||
<div class="layout_top"><?= $this->include($viewDatas['layout']['layout'] . '/top'); ?></div>
|
||||
<!-- Layout Middle Start -->
|
||||
<!-- Layout Middle End -->
|
||||
<div class=" layout_footer"><?= $this->include("{$viewDatas['layout']['template']}/index_footer"); ?></div>
|
||||
<div class="layout_bottom"><?= $this->include($viewDatas['layout']['layout'] . '/bottom'); ?></div>
|
||||
<?= $this->endSection() ?>
|
||||
115
app/Views/layouts/admin/welcome/banner.php
Normal file
115
app/Views/layouts/admin/welcome/banner.php
Normal file
@ -0,0 +1,115 @@
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.card-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.bg-blue {
|
||||
background-color: #337ab7;
|
||||
}
|
||||
|
||||
.bg-yellow {
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.bg-green {
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
|
||||
.bg-red {
|
||||
background-color: #d9534f;
|
||||
}
|
||||
|
||||
.card-footer a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-footer .fa {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.fa-5x {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
.huge {
|
||||
font-size: 2.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bg-detail {
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.bg-detail .bg_blue_font {
|
||||
color: #337ab7;
|
||||
}
|
||||
|
||||
.bg-detail .bg_yellow_font {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.bg-detail .bg_green_font {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
.bg-detail .bg_red_font {
|
||||
color: #d9534f;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<div class="row">
|
||||
<div class="col-lg">
|
||||
<div class="card dashboard-card bg-blue card-clickable" onclick="document.location.href='/admin/board?category=<?= BOARD['CATEGORY']['REQUESTTASK'] ?>&worker_uid=<?= $viewDatas['authContext']->getUID() ?>';">
|
||||
<div class=" card-body">
|
||||
<div class="row">
|
||||
<div class="col-4"><i class="fa fa-comments fa-5x"></i></div>
|
||||
<div class="col-8 text-end">
|
||||
<div class="huge"><?= $viewDatas['boardRequestTaskCount'] ?></div>
|
||||
<div class="bg-blue">요청업무 알림</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" card-footer d-flex justify-content-between align-items-center bg-detail">
|
||||
<span class="bg_blue_font">자세히보기</span><i class="fa fa-arrow-circle-right bg_blue_font"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<div class="card dashboard-card bg-yellow card-clickable" onclick="document.location.href='/admin/customer/service';">
|
||||
<div class=" card-body">
|
||||
<div class="row">
|
||||
<div class="col-4"><i class="fa fa-plus-square-o fa-5x"></i></div>
|
||||
<div class="col-8 text-end">
|
||||
<div class="huge"><?= $viewDatas['newServiceCount'] ?></div>
|
||||
<div>최근 <?= $viewDatas['interval'] ?>일간 신규서버수</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between align-items-center bg-detail">
|
||||
<span class="bg_yellow_font">자세히보기</span><i class="fa fa-arrow-circle-right bg_yellow_font"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<div class="card dashboard-card bg-red card-clickable" onclick="document.location.href='/admin/payment'">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4"><i class="fa fa-support fa-5x"></i></div>
|
||||
<div class="col-8 text-end">
|
||||
<div class="huge"><?= number_format($viewDatas['unPaidTotalCount']) ?>건/<?= number_format($viewDatas['unPaidTotalAmount']) ?>원</div>
|
||||
<div><?= date("Y-m-d") ?> 금일 기준 미납 서비스</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between align-items-center bg-detail">
|
||||
<a href="/admin/payment"><span class=" bg_red_font">자세히보기</span></a><i class="fa fa-arrow-circle-right bg_red_font"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
31
app/Views/layouts/admin/welcome/index.php
Normal file
31
app/Views/layouts/admin/welcome/index.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?= $this->extend($viewDatas['layout']['layout']) ?>
|
||||
<?= $this->section('content') ?>
|
||||
<!-- Layout Middle Start -->
|
||||
<div class="layout_top"><?= $this->include($viewDatas['layout']['layout'] . '/top'); ?></div>
|
||||
<!-- Layout Middle Start -->
|
||||
<table class="layout_middle">
|
||||
<tr>
|
||||
<td class="layout_left">
|
||||
<!-- Layout Left Start -->
|
||||
<?= $this->include($viewDatas['layout']['layout'] . '/left_menu'); ?>
|
||||
<!-- Layout Left End -->
|
||||
</td>
|
||||
<td class="layout_right">
|
||||
<!-- Layout Right Start -->
|
||||
<?= $this->include("{$viewDatas['layout']['path']}/welcome/banner"); ?>
|
||||
<div class="row align-items-start mt-3">
|
||||
<div class="col-8">
|
||||
<?= $this->include("{$viewDatas['layout']['path']}/welcome/total_service"); ?>
|
||||
<?= $this->include("{$viewDatas['layout']['path']}/welcome/new_service"); ?>
|
||||
<?= $this->include("{$viewDatas['layout']['path']}/welcome/stock"); ?>
|
||||
</div>
|
||||
<div class="col-4"><?= $this->include("{$viewDatas['layout']['path']}/welcome/mylog"); ?></div>
|
||||
</div>
|
||||
<!-- Layout Right End -->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- Layout Middle End -->
|
||||
<div class=" layout_footer"><?= $this->include("{$viewDatas['layout']['template']}/index_footer"); ?></div>
|
||||
<div class="layout_bottom"><?= $this->include($viewDatas['layout']['layout'] . '/bottom'); ?></div>
|
||||
<?= $this->endSection() ?>
|
||||
13
app/Views/layouts/admin/welcome/mylog.php
Normal file
13
app/Views/layouts/admin/welcome/mylog.php
Normal file
@ -0,0 +1,13 @@
|
||||
<div class="layout_header">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<span class="nav-item navbar-brand" aria-current="page">
|
||||
<h4> <?= icon('SETUP') ?>작업내역 </h4>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="border-left: 1px solid black; border-right: 1px solid black; padding:20px;">
|
||||
<?= view_cell("\App\Cells\MylogCell::dashboard") ?>
|
||||
</div>
|
||||
<div class="layout_footer"></div>
|
||||
39
app/Views/layouts/admin/welcome/new_service.php
Normal file
39
app/Views/layouts/admin/welcome/new_service.php
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="layout_header mt-3">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<span class="nav-item navbar-brand" aria-current="page">
|
||||
<h4> <?= icon('CHART') ?> 최신 신규 서비스 현황 </h4>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="border-left: 1px solid black; border-right: 1px solid black; padding:20px;">
|
||||
<table class="table table-bordered table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr class="text-center">
|
||||
<th>사이트</th>
|
||||
<th>업체명</th>
|
||||
<th>
|
||||
<span class="float-start rounded border border-primary" style="cursor:pointer;"
|
||||
onclick="copyServerPartsToClipboard()">ALL 📋</span> 장비번호 / 스위치정보 / IP정보 / CS정보
|
||||
</th>
|
||||
<th>등록자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($viewDatas['newServiceEntities'] as $entity): ?>
|
||||
<?php $viewDatas['entity'] = $entity ?>
|
||||
<tr class="text-center">
|
||||
<td><?= SITES[$entity->getSite()] ?></td>
|
||||
<td nowrap><?= $viewDatas['helper']->getFieldView('clientinfo_uid', $entity->getClientInfoUid(), $viewDatas) ?>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<?= $viewDatas['helper']->getFieldView('serverinfo_uid', $entity->getServerInfoUid(), $viewDatas) ?></td>
|
||||
<td nowrap><?= $viewDatas['helper']->getFieldView('user_uid', $entity->getUserUid(), $viewDatas) ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="layout_footer"></div>
|
||||
<script src="/js/admin/clipboard.js"></script>
|
||||
24
app/Views/layouts/admin/welcome/stock.php
Normal file
24
app/Views/layouts/admin/welcome/stock.php
Normal file
@ -0,0 +1,24 @@
|
||||
<div class="layout_header" style="margin-top:20px;">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<span class="nav-item navbar-brand" aria-current="page">
|
||||
<h4> <?= icon('SETUP') ?>재고현황 </h4>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="border-left: 1px solid black; border-right: 1px solid black; padding:20px;">
|
||||
<table class="table table-bordered table-striped">
|
||||
<tr>
|
||||
<th class="text-center" width="33%">사용 서버</th>
|
||||
<th class="text-center" width="33%">메모리 재고</th>
|
||||
<th class="text-center">저장장치 재고</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?= view_cell("\App\Cells\Equipment\CHASSISCell::stock") ?></td>
|
||||
<td><?= view_cell("\App\Cells\Part\RAMCell::stock") ?></td>
|
||||
<td><?= view_cell("\App\Cells\Part\DISKCell::stock") ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="layout_footer"></div>
|
||||
45
app/Views/layouts/admin/welcome/total_service.php
Normal file
45
app/Views/layouts/admin/welcome/total_service.php
Normal file
@ -0,0 +1,45 @@
|
||||
<div class="layout_header">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<span class="nav-item navbar-brand" aria-current="page">
|
||||
<h4> <?= icon('CHART') ?> 전체 서비스 현황 </h4>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="border-left: 1px solid black; border-right: 1px solid black; padding:20px;">
|
||||
<table class="table table-bordered table-hover table-align-middle">
|
||||
<tr class="text-center">
|
||||
<th rowspan="2" class="bg-light">사이트</th>
|
||||
<th colspan="2" class="bg-light">일반</th>
|
||||
<th colspan="2" class="bg-light">방어</th>
|
||||
<th colspan="2" class="bg-light">전용</th>
|
||||
<th colspan="2" class="bg-light">대체</th>
|
||||
<th colspan="2" class="bg-light">VPN</th>
|
||||
<th colspan="2" class="bg-light">이벤트</th>
|
||||
<th colspan="2" class="bg-light">테스트</th>
|
||||
<th colspan="3" class="bg-light">합계</th>
|
||||
</tr>
|
||||
<tr class="text-center">
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">도쿄</th>
|
||||
<th class="bg-light">치바</th>
|
||||
<th class="bg-light">합계</th>
|
||||
</tr>
|
||||
<?= view_cell("\App\Cells\Equipment\ServerCell::totalCountDashboard") ?>
|
||||
</table>
|
||||
</div>
|
||||
<div class="layout_footer"></div>
|
||||
@ -1,2 +0,0 @@
|
||||
<!-- left menu start -->
|
||||
<!-- left menu end -->
|
||||
@ -1,130 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= esc($title) ?></title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;700;800&family=Noto+Sans+KR:wght@400;700;900&display=swap"
|
||||
rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Noto Sans KR', sans-serif;
|
||||
}
|
||||
|
||||
.bg-grid {
|
||||
background-image: radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.05) 1px, transparent 0);
|
||||
background-size: 40px 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-[#101922] text-white selection:bg-blue-500/30">
|
||||
|
||||
<!-- Navbar -->
|
||||
<nav class="fixed w-full z-50 bg-[#101922]/80 backdrop-blur-md border-b border-white/5">
|
||||
<div class="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-[#2b8cee] p-2 rounded-lg">
|
||||
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="text-xl font-black uppercase tracking-tighter">Daemon-IDC</span>
|
||||
</div>
|
||||
<div class="hidden md:flex gap-10 text-sm font-bold text-slate-400">
|
||||
<a href="#" class="hover:text-white transition-colors">서비스</a>
|
||||
<a href="#" class="hover:text-white transition-colors">요금제</a>
|
||||
<a href="#" class="hover:text-white transition-colors">고객지원</a>
|
||||
<button class="bg-[#2b8cee] text-white px-5 py-2 rounded-lg -mt-1">로그인</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<header class="relative min-h-screen flex items-center pt-20 overflow-hidden">
|
||||
<div class="absolute inset-0 z-0">
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-[#101922] via-[#101922]/90 to-transparent z-10"></div>
|
||||
<!-- 핫링크 테스트를 위한 컨트롤러 경로 이미지 예시 (가상) -->
|
||||
<img src="https://images.unsplash.com/photo-1558494949-ef010cbdcc51?auto=format&fit=crop&q=80&w=2000"
|
||||
alt="IDC Background" class="w-full h-full object-cover opacity-30">
|
||||
</div>
|
||||
<div class="relative z-20 max-w-7xl mx-auto px-6 w-full">
|
||||
<div class="max-w-2xl space-y-8">
|
||||
<div
|
||||
class="inline-block px-3 py-1 bg-[#2b8cee]/10 border border-[#2b8cee]/30 text-[#2b8cee] text-xs font-bold rounded-full uppercase">
|
||||
CodeIgniter 4.6.4 Powered</div>
|
||||
<h1 class="text-6xl md:text-7xl font-black leading-tight tracking-tighter">일본 비즈니스의<br><span
|
||||
class="text-[#2b8cee]">견고한 뿌리</span></h1>
|
||||
<p class="text-xl text-slate-400 font-medium leading-relaxed">PHP 8.3 기반의 초고속 백엔드와 일본 현지 데이터센터의 결합. 가장
|
||||
신뢰받는 인프라를 지금 바로 경험하십시오.</p>
|
||||
<div class="flex gap-4">
|
||||
<button
|
||||
class="bg-[#2b8cee] px-8 py-4 rounded-xl font-black shadow-lg shadow-[#2b8cee]/30 hover:scale-105 transition-transform">서비스
|
||||
상담하기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Services Grid -->
|
||||
<section class="py-32 bg-grid">
|
||||
<div class="max-w-7xl mx-auto px-6">
|
||||
<h2 class="text-3xl font-black mb-16 text-center">전문적인 인프라 솔루션</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<?php foreach ($services as $s): ?>
|
||||
<div
|
||||
class="p-8 bg-white/5 border border-white/10 rounded-2xl hover:border-[#2b8cee]/50 transition-colors group">
|
||||
<div
|
||||
class="w-12 h-12 bg-[#2b8cee]/10 rounded-xl mb-6 flex items-center justify-center text-[#2b8cee]">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3"><?= esc($s['title']) ?></h3>
|
||||
<p class="text-slate-500 text-sm"><?= esc($s['desc']) ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Pricing -->
|
||||
<section class="py-32 bg-[#0b1117]">
|
||||
<div class="max-w-7xl mx-auto px-6 text-center">
|
||||
<h2 class="text-3xl font-black mb-16">투명한 요금제</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 text-left">
|
||||
<?php foreach ($pricing as $p): ?>
|
||||
<div
|
||||
class="p-8 rounded-2xl bg-[#101922] border <?= $p['pop'] ? 'border-[#2b8cee] ring-1 ring-[#2b8cee]' : 'border-white/5' ?> relative overflow-hidden">
|
||||
<?php if ($p['pop']): ?>
|
||||
<div
|
||||
class="absolute top-0 right-0 bg-[#2b8cee] text-white text-[10px] px-3 py-1 font-bold uppercase">
|
||||
Popular</div><?php endif; ?>
|
||||
<h4 class="text-slate-400 font-bold mb-4"><?= esc($p['name']) ?></h4>
|
||||
<div class="flex items-baseline gap-1 mb-8">
|
||||
<span class="text-4xl font-black"><?= esc($p['price']) ?></span>
|
||||
<span class="text-slate-500 font-bold"><?= esc($p['unit']) ?></span>
|
||||
</div>
|
||||
<button
|
||||
class="w-full py-3 rounded-lg font-bold <?= $p['pop'] ? 'bg-[#2b8cee] text-white' : 'bg-slate-800 text-slate-300' ?> hover:opacity-90 transition-opacity">선택하기</button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer
|
||||
class="py-20 border-t border-white/5 text-center text-slate-600 font-bold text-xs uppercase tracking-widest">
|
||||
© 2024 DAEMON-IDC. Built with CodeIgniter 4.6.4 & PHP 8.3.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user