Merge pull request 'nim_style' (#3) from nim_style into master

Reviewed-on: #3
pull/5/head
nimtaurel 2025-03-03 13:50:01 +00:00
commit 54fcb17a6a
19 changed files with 904 additions and 308 deletions

26
app/components/Grid.php Normal file
View File

@ -0,0 +1,26 @@
<?php
/*
* Copyright (c) 2023.
*
* A.Сапаргалиев
* ТОО "Дизайн лаборатория А7"
* Астана
*/
namespace App\components;
use A7kz\Platform\Modules\Platform\Core\Services\Base\Component;
class Grid extends Component
{
public function __construct(array $params = [])
{
parent::__construct($params, '');
$this->template = "components.grid";
}
public function render(){
return view($this->template, $this->params)->render();
}
}

View File

@ -0,0 +1,91 @@
<?php
/*
* Copyright (c) 2023.
*
* A.Сапаргалиев
* ТОО "Дизайн лаборатория А7"
* Астана
*/
namespace App\components;
use A7kz\Platform\Modules\Platform\Acl\Facades\Acl;
use A7kz\Platform\Modules\Platform\Core\Facades\Logic;
use A7kz\Platform\Modules\Platform\Core\Services\Base\Component;
class HeadActions extends Component
{
const TYPE_LOGIC = 'logic';
const TYPE_ACTION = 'action';
const ACCESS_HANDLER_KEY = 'access_handler';
public array $actions = [];
public function __construct(array $params = [])
{
parent::__construct($params, $module = '');
$this->template = "components.head_actions";
if ($this->app->action['head'] ?? false) {
$this->prepareHeadActions();
}
$this->setParam('actions', $this->actions);
}
public function render(){
return view($this->template, $this->params)->render();
}
protected function prepareHeadActions(): void
{
$this->actions = $this->app->action['head'];
foreach ($this->actions as $action => $property) {
$this->actions[$action]->show = false;
if((!$this->app->isReadonly() || !isset($property->readonly) || !$property->readonly) && $this->app->isReadonly()) {
continue;
}
switch ($property?->type) {
case self::TYPE_LOGIC:
$this->handleLogic($action, $property);
break;
case self::TYPE_ACTION:
default:
$this->handleAction($action, $property);
break;
}
}
}
protected function handleLogic(string $action, $property): void
{
if (isset($property->{self::ACCESS_HANDLER_KEY})) {
$show = $this->handleActionAccess($action, $property->{self::ACCESS_HANDLER_KEY});
} else {
$show = Acl::isInRoleArray($property->access ?? []);
}
$this->actions[$action]->url = $this->app->getPath() . '?logic=' . $action;
$this->actions[$action]->show = $show;
}
public function handleAction(string $action, $property): void
{
if (isset($property->{self::ACCESS_HANDLER_KEY})) {
$show = $this->handleActionAccess($action, $property->{self::ACCESS_HANDLER_KEY});
} else {
$show = true;
}
$this->actions[$action]->url = $this->app->getPath() . '/' . $action;
$this->actions[$action]->show = $show;
}
protected function handleActionAccess(string $action, string $handler): bool
{
$processorResult = Logic::handle($handler, $this->app, ['action' => $action]);
return $processorResult->getStatus();
}
}

592
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,26 +5,43 @@
<div class="log-reg-content">
<div class="d-flex justify-content-between">
<div class="item-nav-lang">
<div class="dropdown">
<div class="item-nav lang">
<div class="dropdown">
<span class="dropdown-trigger" data-bs-toggle="dropdown">
<i class="bi bi-translate"></i>
{{__(app()->getLocale())}}
@if(app()->getLocale() == 'ru')
RU <img src="{{asset('img/ru.png')}}" alt="ru">
@elseif(app()->getLocale() == 'kz')
KZ <img src="{{asset('img/kz.png')}}" alt="kz">
@elseif(app()->getLocale() == 'en')
EN <img src="{{asset('img/en.png')}}" alt="en">
@endif
</span>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
@if(app()->getLocale() != \A7kz\Platform\Enums\Languages::Ru)
<li><a class="dropdown-item"
href="{{ \A7kz\Platform\Modules\Platform\Translate\Helpers\TranslateHelper::changeUrlLanguage(\A7kz\Platform\Enums\Languages::Ru) }}"><span>@lang('ru')</span></a>
href="{{ \A7kz\Platform\Modules\Platform\Translate\Helpers\TranslateHelper::changeUrlLanguage(\A7kz\Platform\Enums\Languages::Ru) }}">RU
<img src="{{asset('img/ru.png')}}" alt="ru"></a>
</li>
@endif
@if(app()->getLocale() != \A7kz\Platform\Enums\Languages::Kz)
<li><a class="dropdown-item"
href="{{ \A7kz\Platform\Modules\Platform\Translate\Helpers\TranslateHelper::changeUrlLanguage(\A7kz\Platform\Enums\Languages::Kz) }}"><span>@lang('kz')</span></a>
href="{{ \A7kz\Platform\Modules\Platform\Translate\Helpers\TranslateHelper::changeUrlLanguage(\A7kz\Platform\Enums\Languages::Kz) }}">KZ
<img src="{{asset('img/kz.png')}}" alt="kz"></a>
</li>
@endif
@if(app()->getLocale() != \A7kz\Platform\Enums\Languages::En)
<li><a class="dropdown-item"
href="{{ \A7kz\Platform\Modules\Platform\Translate\Helpers\TranslateHelper::changeUrlLanguage(\A7kz\Platform\Enums\Languages::En) }}">EN
<img src="{{asset('img/en.png')}}" alt="en"></a>
</li>
@endif
</ul>
</div>
</div>
<img src="{{asset('img/logo.svg')}}" alt="logo" class="logo-log">
</div>
</div>
<img src="{{asset('img/logo.png')}}" alt="logo" class="logo-log">
</div>
<form method="POST" action="{{ lurl('login') }}">
@csrf
@ -51,37 +68,24 @@
<div class="invalid-feedback form-text text-danger" role="alert">{{ $error['password'] }}</div>
@endif
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" name="remember"
id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember"> {{ __('Remember Me') }}</label>
<div class="reset-container">
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ lurl('/password/reset') }}">
{{ __('Забыли пароль?') }}
</a>
@endif
</div>
<div class="d-flex align-items-center justify-content-center">
<div class="d-flex align-items-center justify-content-between">
<button type="submit" class="btn btn-success">
{{ __('Войти') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ lurl('/password/reset') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</form>
<div class="contact-us-wrapper d-flex flex-column justify-content-center align-items-center">
<div class="phone-cont d-flex flex-column align-items-center mb-1">
<p>7 776 350 41 41<span>({{ __('звонок бесплатный') }})</span></p>
<a class="header-phone" href="tel:87763504141"><i class="bi bi-telephone-fill"></i> +7 776 350 41 41</a>
</div>
<div class="line"></div>
<div class="d-flex flex-row justify-content-between" style="column-gap: 10px">
<div class="d-flex flex-column align-items-start justify-content-start">
<p><i class="bi bi-envelope-fill"></i> support@esep.kz</p>
</div>
<div class="d-flex flex-column align-items-end justify-content-center">
<p><i class="bi bi-clock-fill"></i> {{ __('Часы работы') }}</p>
<p>{{ __('ПН—ПТ') }} 9:00—18:00</p>
</div>
</div>
</div>
</div>
</div>

View File

@ -3,6 +3,7 @@
"name": "pipicar.auto",
"type": "crud",
"title": "Автомобили",
"withHeader": false,
"data": {
"table": "pipi_auto",
"pk": "id",
@ -154,7 +155,7 @@
"ui": {
"grid": {
"title": "Автомобили",
"template": "app.base.crud.grid",
"component": "App.components.grid",
"cols": [
{
"name": "code",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

BIN
public/img/main-bg-full.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

View File

@ -111,6 +111,7 @@
"/img/logo-white.png": "/img/logo-white.png",
"/img/logo.png": "/img/logo.png",
"/img/logo.svg": "/img/logo.svg",
"/img/main-bg-full.jpg": "/img/main-bg-full.jpg",
"/img/ru.png": "/img/ru.png",
"/img/sidebar-bg.jpg": "/img/sidebar-bg.jpg",
"/lib/edit_area/autocompletion.js": "/lib/edit_area/autocompletion.js",

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

View File

@ -17,6 +17,10 @@
.left-content, .right-content{
display: flex;
align-items: center;
.btn {
font-size: 0.8rem;
padding: 0.6rem 0.8rem;
}
}
.right-content{
.btn{

View File

@ -6,20 +6,135 @@
height: 100%;
z-index: 99999;
display: flex;
justify-content: flex-end;
background-image: url('../../img/bg-log-reg.png');
justify-content: flex-start;
background-image: url('../../img/main-bg-full.jpg');
background-position: center;
background-size: cover;
padding: 2rem;
.log-reg-content{
width: 100%;
max-width: 500px;
height: 100vh;
height: calc(100vh - 4rem);
background-color: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 50px;
overflow: auto;
border-radius: 25px;
-webkit-box-shadow: 1px -2px 17px -5px rgba(179, 179, 179, 0.76);
-moz-box-shadow: 1px -2px 17px -5px rgba(179, 179, 179, 0.76);
box-shadow: 1px -2px 17px -5px rgba(179, 179, 179, 0.76);
.reset-container {
margin: 0 0 15px;
padding: 0;
display: flex;
justify-content: flex-end;
align-items: center;
a {
text-decoration: none;
color: $gray-color;
padding: 0;
transition: all 0.3s ease-in-out;
font-size: 0.875rem;
text-align: end;
}
a:hover {
color: $secondary-color;
}
}
.btn {
width: 100%;
}
.logo-log {
width: 170px;
height: 40.5px;
}
.header-phone {
font-size: 16px;
color: #01b0e8;
display: inline-block;
font-weight: 700;
margin-left: 10px;
text-decoration: none;
}
.dropdown-trigger {
text-transform: uppercase;
text-decoration: none;
height: 49px;
padding: 0 20px;
display: flex;
align-items: center;
cursor: pointer;
justify-content: center;
transition: 0.15s all ease;
font-size: 1rem;
&:hover {
color: $primary-color;
}
img {
width: 20px;
height: 20px;
margin: 0 8px;
}
}
.dropdown-trigger::after {
display: inline-block;
margin-left: .255em;
vertical-align: .255em;
content: "";
border-top: .3em solid;
border-right: .3em solid transparent;
border-bottom: 0;
border-left: .3em solid transparent;
}
.dropdown-menu {
img {
width: 20px;
height: 20px;
margin: 0 8px;
}
.dropdown-menu {
position: absolute;
z-index: 1000;
display: none;
min-width: 10rem;
padding: .5rem 0;
margin: 0;
font-size: 1rem;
color: #212529;
text-align: left;
list-style: none;
background-color: #fff;
background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, .15);
border-radius: .25rem;
}
.dropdown-item {
padding: 5px 10px 5px 17px;
display: flex;
align-items: center;
color: $gray-color;
font-weight: 700;
transition: 0.15s all ease;
&:hover {
background-color: #01b0e836;
color: $text-color;
}
}
}
}
form{

View File

@ -66,6 +66,7 @@
&:hover {
color: $primary-color;
}
}
.dropdown-trigger::after {
display: inline-block;

View File

@ -6,6 +6,7 @@
background:url("../../img/sidebar-bg.jpg") no-repeat;
height: 100%;
transition: 0.15s allease;
background-size: cover;
ul {
list-style: none;
padding-inline-start: 0;
@ -40,11 +41,14 @@
min-height: 40px;
font-size: 1rem;
cursor: pointer;
transition: 0.15s all ease;
transition: 0.3s all ease;
&:hover {
color: white;
background-color: $text-color!important;
background-color: $border-color!important;
}
&:focus-visible {
border: none;
}
.menu-icon {
@ -78,8 +82,8 @@
.has-more {
transform: rotate(0);
background-color: $primary-bg-color;
color: $primary-color;
background-color: $border-color;
color: $text-color;
}
}
}

View File

@ -83,6 +83,8 @@
nav {
padding: 10px;
display: flex;
justify-content: center;
ul {
margin: 0;

View File

@ -9,6 +9,7 @@ $primary-hover: #0193c7;
$success-color: #07b385;
$danger-color: #dc3545;
$secondary-color: #424B4B;
$light-color: #eeeeee;
$primary-bg-color: rgba(11, 107, 251, 0.2);
$success-bg-color: rgba(7, 179, 133, 0.2);

View File

@ -0,0 +1,247 @@
<?php
/*
* Copyright (c) 2022.
* Aidyn Sapargaliyev
* Nur-Sultan (Astana), Kazakhstan
* ТОО Дизайн лаборатория А7
* https://a7.kz
*/
/**
* @var \A7kz\Platform\Modules\Platform\Core\Services\Application\CrudApplication $app
*/
use A7kz\Platform\Modules\Platform\Core\Services\Data\DataTypes;
use Illuminate\Support\Carbon;
$show = false;
$actions = $app->getActions();
if (isset($actions->show) && \A7kz\Platform\Modules\Platform\Acl\Facades\Acl::isCanRun($app->getName(), 'show')) {
$show = true;
}
?>
<style>
.rowStyleStatic td {}
</style>
<x-component name="Js.Before" :params="['app'=>$app]" :module="$module"/>
{!! $app->show_message() !!}
<div class="global-actions">
<div class="left-content">
@if(!is_null($app->filter))
<span class="btn btn-primary filter-trigger">{{__("Фильтр")}}</span>
@endif
<h5 class="global-title px-4">{{__($app->getUiTitle("grid"))}}</h5>
</div>
<div class="right-content">
<x-component name="App.components.HeadActions" :params="['app'=>$app]" :module="$module"/>
</div>
</div>
<div>
{!! $app->filter() !!}
</div>
<div class="table-container @if($app->data->isSubCrud() ?? false)subcrud-table @endif">
<table class="table table-hover">
<thead>
<tr>
@foreach($app->cols as $col)
<th>
<a href="@if($col->name == $app->data->getSortField())
@if($app->data->getSortOrder() == 'asc')
{{$app->getPath().'?sort=desc-'.$col->name.'&page='.$app->data->getPage()}}
@else
{{$app->getPath().'?sort=asc-'.$col->name.'&page='.$app->data->getPage()}}
@endif
@else
{{$app->getPath().'?sort=asc-'.$col->name.'&page='.$app->data->getPage()}}
@endif">
{{__($col->caption)}}
@if($col->name == $app->data->getSortField())
@if($app->data->getSortOrder() == 'asc')
<i class="bi bi-arrow-up"></i>
@else
<i class="bi bi-arrow-down"></i>
@endif
@endif
</a>
</th>
@endforeach
@if(!$app->isReadonly())
@if(isset($app->action['row']))
<th></th>
@endif
@endif
</tr>
</thead>
<tbody>
@foreach($app->rows as $row)
@php
$href = !isset($targetLink) ?
$app->getPath()."/show?pk=".$row->{$app->data->getPk()} :
lurl("app/".$targetLink->application."/show?pk=".$row->{$app->data->getPk()});
@endphp
<tr @if(\A7kz\Platform\Modules\Platform\Acl\Facades\Acl::isCanRun($app->getName(), 'show')) style="cursor: pointer;"
class="grid-table-row" data-href="{{$href}}" @endif>
@foreach($app->cols as $col)
<td @if(isset($row->style)) style="{{$row->style}}" @endif>
@if($app->data->fields[$col->name]->getType() == DataTypes::Boolean)
@if($row->{$col->name})
<i class="yes bi bi-check-circle-fill"></i>
@elseif(isset($app->data->fields[$col->name]->nullable) && is_null($row->{$col->name}))
<i class="wait bi bi-hourglass-split"></i>
@else
<i class="no bi bi-x-circle-fill"></i>
@endif
@elseif($app->data->fields[$col->name]->getType() == DataTypes::Date)
{!! $row->{$col->name}?\Illuminate\Support\Carbon::parse($row->{$col->name})->format('d.m.Y'):'' !!}
@elseif($app->data->fields[$col->name]->getType() == DataTypes::DateTime)
{!! $row->{$col->name}?\Illuminate\Support\Carbon::parse($row->{$col->name})->format('d.m.Y H:i:s'):'' !!}
@elseif($app->data->fields[$col->name]->getType() == DataTypes::Month)
{!! $row->{$col->name}?\A7kz\Platform\Models\Month::get($row->{$col->name}):'' !!}
@elseif($app->data->fields[$col->name]->getType() == DataTypes::Wysiwyg)
{!! $row->{$col->name} !!}
@elseif($app->data->fields[$col->name]->getType() == DataTypes::Foreign)
@php
$str = '';
if (isset($row->{$col->name . "_list_class_val"})) {
$str = $row->{$col->name . "_list_class_val"};
} elseif (!empty($row->{$col->name})) {
$templateKey = isset($app->data->fields[$col->name]->grid_template)
? 'grid_template'
: (isset($app->data->fields[$col->name]->template) ? 'template' : null);
if ($templateKey) {
$str = $app->data->fields[$col->name]->{$templateKey};
$str = str_replace('{lk}', app()->getLocale(), $str);
foreach ($app->data->fields[$col->name]->display as $item) {
$str = str_replace("{" . $item . "}", $row->{$col->name . "_" . $item}, $str);
}
$str = preg_replace_callback('/\{(.*?)\}/', function ($matches) use ($row) {
return $row->{$matches[1]} ?? '';
}, $str);
} else {
$str = '';
foreach ($app->data->fields[$col->name]->display as $item) {
$str .= $row->{$col->name . "_" . $item} . " ";
}
}
}
@endphp
{{ $str }}
@elseif($app->data->fields[$col->name]->getType() == DataTypes::Json)
{{var_dump(json_decode($row->{$col->name},true))}}
@elseif($app->data->fields[$col->name]->getType() == DataTypes::Combined)
{{ data_get(json_decode($row->{$app->data->fields[$col->name]->getDbFieldName()},true), $app->data->fields[$col->name]->getFieldName()) }}
@elseif($app->data->fields[$col->name]->getType() == DataTypes::ExpirationStatus)
@php
$currentDate = Carbon::now();
$expirationDate = $row->{$col->name} != '' ? Carbon::parse($row->{$col->name}) : null;
$creationDate = $app->data->fields[$col->name]->started_at_field
? Carbon::parse($row->{$app->data->fields[$col->name]->started_at_field} ?? null)
: null;
$isActive = match (true) {
is_null($expirationDate) && is_null($creationDate) => true,
is_null($creationDate) => $currentDate->lt($expirationDate),
is_null($expirationDate) => $currentDate->gte($creationDate),
default => ($currentDate->lt($expirationDate) and $currentDate->gte($creationDate)),
};
$status = $isActive ? 'active' : 'inactive';
$text = $app->data->fields[$col->name]->text[$status] ??
($isActive ? __('Активно') : __('Неактивно'));
$icon = $isActive
? '<i class="yes bi bi-check-circle-fill"></i>'
: '<i class="no bi bi-x-circle-fill"></i>';
echo "$icon <span class='status-label'>$text</span>";
@endphp
@else
@php
$truncateLength = $col->truncate_length ?? mb_strlen($row->{ $col->name });
if (mb_strlen($row->{ $col->name }) > $truncateLength) {
$row->{ $col->name } = mb_substr($row->{ $col->name }, 0, $truncateLength) . '...';
}
@endphp
@if($col->link ?? false)
<a href="{{ lurl(sprintf($col->link, ...array_map(fn($repl) => $row->{$repl}, $col->replace))) }}">
{{ $row->{ $col->name } }}
</a>
@else
{{ $row->{ $col->name } }}
@endif
@endif
</td>
@endforeach
@if(!$app->isReadonly())
@if(isset($app->action['row']))
<td class="td-actions"
@if(isset($app->action['row'])) style="width: {{count($app->action['row'])*40}}px" @endif>
@foreach($app->action['row'] as $action=>$actionProperties)
@if(!is_null($actionProperties))
<x-component name="Buttons.GridActionButton"
:params="[
'app' => $app,
'row' => $row,
'action' => $action,
'actionProps' => $actionProperties,
'label' => $actionProperties->label,
'btn' => $actionProperties->btn ?? null,
'icon' => $actionProperties->icon ?? null
]"
/>
@endif
@endforeach
</td>
@endif
@endif
</tr>
@endforeach
</tbody>
<tfoot>
@if($app->rows->total() > $app->data->limit)
<tr>
<td class="td-pagination" colspan="{{count($app->cols)+1}}">
{{$app->rows->links()}}
</td>
</tr>
@endif
</tfoot>
</table>
</div>
<x-component name="Js.After" :params="['app'=>$app]" :module="$module"/>
<x-component name="Additional.ImportAction" :params="['app'=>$app]" :module="$module"/>
@if(isset($app->getConfig()?->ui?->grid?->inject_component))
@foreach($app->getCOnfig()->ui->grid->inject_component as $inject_component)
<x-component :name="$inject_component" :module="$module" :params="['app'=>$app]"/>
@endforeach
@endif
@if(isset($app->getConfig()?->ui?->grid?->inject_component))
@if(!empty($app->getConfig()->ui->grid->on_render_emit_functions))
<x-component name="Js.OnRenderEmitFunctions" :module="$module"
:params="['app' => $app, 'emitFunctions' => $app->getConfig()->ui->grid->on_render_emit_functions]"
/>
@endif
@endif
<script>
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".grid-table-row").forEach((el) => {
el.addEventListener("click", (event) => {
if (event.target.classList.contains("btn") || event.target.parentNode.classList.contains("btn")) {
return;
}
else {
window.open(el.dataset.href, '_self');
}
});
});
});
</script>

View File

@ -0,0 +1,27 @@
<?php
$actions ??= [];
?>
@foreach($actions as $action=>$property)
@php
$type = $property?->type ?? '';
@endphp
@switch($type)
@case("custom")
<x-component name="{{ $property->component }}" :params="['app' => $app]"/>
@break
@default
@if($property->show ?? false)
<x-component name="Buttons.HeadActionButton" :params="[
'app' => $app,
'actionProps' => $property ?? null,
'label' => $property->label,
'btn' => $property->btn ?? null,
]"/>
@endif
@break
@endswitch
@endforeach