Compare commits
3 Commits
cda29ccaf9
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 30f2d4444c | |||
| 470a08ba04 | |||
| 4a3751dfce |
@@ -1,59 +1,32 @@
|
|||||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
# Magic 2.0
|
||||||
|
Попытка ретроспективно описать этапы разработки проекта Magic 2.0.
|
||||||
|
Изначально проект реализовывался сумбурно, без структурированной фиксации каждой доработки в расчете описать всю историю перед релизом.
|
||||||
|
|
||||||
<p align="center">
|
## Ход работ:
|
||||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
Есть текущая версия Laravel со <u>всеми</u> реализованными фичами (а также с комментариями, тестовыми настройками и прочим мусором). Была развернута голая Laravel, созданы ветки и в них добавлялись скрипты из текущей версии Laravel по принципу реализованных функциональностей. Версии Laravel отличаются, поэтому некоторые конфиги из коробки могут отличаться от конфигов из текущей версии (впрочем, вроде не критично). Также я приложил excel файл (new_magic в ветку master) к репозиторию, в котором перечислил по каким веткам я разбросал файлы, а к некоторым файлам оставил комментарии
|
||||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
|
||||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
|
||||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## About Laravel
|
Я знаю, что это не лучший способ фиксировать историчность разработки, но выбор был между отсутствием истории и такой историей. Даже если вы не будете использовать мои разработки как основу для Magic 2.0, по крайней мере пробегитесь глазами по архитектуре и почитайте комментарии. Некоторые фичи полность готовы и могут быть перенесены, другие могут сэкономить время хотя бы за счет готового описания
|
||||||
|
|
||||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
Не забывайте, что в Confulence тоже кое-что из реализованного уже описано
|
||||||
|
|
||||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
_Заранее извиняюсь за сумбурность и неработоспособность чего-то - можете лишить меня годовой премии_
|
||||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
|
||||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
|
||||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
|
||||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
|
||||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
|
||||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
|
||||||
|
|
||||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
## Ветки
|
||||||
|
Ниже описание некоторых веток. В основном, они называются очевидным образом, но некоторые требуют пояснения
|
||||||
|
+ all_changes - по сути это текущая версия Magic 2.0 со всеми изменениям
|
||||||
|
+ master - голая версия Laravel, от которой наследовались все фичевые ветки
|
||||||
|
+ doubtfull - сомнительная ветка. Не уверен, что скрипты из нее нужны для запуска проекта. Возможно, конфиги, которые в новой версии Laravel не требуются для запуска
|
||||||
|
+ trash - мусорная ветка. Скрипты отсюда почти наверняка не нужны для работы проекта. Например, тестовые скрипты или пустые скрипты архитектурных сущностей, которыми мы не пользовались (вроде сидеры к ним относятся). В новых версиях ларавель они создаются не из коробки, а при вызове команды на создание сущностей
|
||||||
|
+ db_tables - набор конфигов sql таблиц и дампов с имеющимися на текущий момент записями
|
||||||
|
+ ветки, начинающиеся на **front_** или **react_** - предполагаю, что их должен проанализировать фронтендер
|
||||||
|
+ laravel_core - основные конфиги для запуска проекта
|
||||||
|
|
||||||
## Learning Laravel
|
## Сборка
|
||||||
|
Ниже опишу как бы я смотрел все эти изменения
|
||||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application.
|
1. Начать с версии из ветки all_changes - там вся имеющаяся функциональность
|
||||||
|
2. Все таблицы из db_tables экспортировать в свою локальную бд
|
||||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
3. Просмотрел бы верхнеуровнево все core ветки, начиная от конфигов и core функций типа logging
|
||||||
|
4. На голую версия накинул бы ветку laravel_core и doubtfull
|
||||||
## Laravel Sponsors
|
5. Начал бы накидывать постепенно изменения из веток и смотреть как работает платформа. Я постарался в ветках хранить только ту информацию, которая нужна для работы конкреной функциональности, чтобы вы могли изолированно их посмотреть и даже запустить для тестов перед мержем с master. К сожалению у меня не было времени тестить каждую ветку отдельно
|
||||||
|
6. В последнюю очередь накинул бы изменения из веток с реализацией страниц: magic_app_menu и taxi-app
|
||||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
7. Опциально: можно начать удалять скрипты из ветки doubtfull и смотреть работает ли все как надо, чтобы облегчить платформу от ненужных скриптов
|
||||||
|
|
||||||
### Premium Partners
|
|
||||||
|
|
||||||
- **[Vehikl](https://vehikl.com)**
|
|
||||||
- **[Tighten Co.](https://tighten.co)**
|
|
||||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
|
||||||
- **[64 Robots](https://64robots.com)**
|
|
||||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
|
|
||||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
|
||||||
- **[Redberry](https://redberry.international/laravel-development)**
|
|
||||||
- **[Active Logic](https://activelogic.com)**
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
|
||||||
|
|
||||||
## Security Vulnerabilities
|
|
||||||
|
|
||||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Models;
|
|
||||||
|
|
||||||
class MenuController extends Controller
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->userLogin = 'dgavrilov';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Получение всех записей из таблицы с приложениями для меню
|
|
||||||
* TODO
|
|
||||||
* передавай флаг isActive и используй его для получения записей с приложениями меню архивных и не архивных
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getApps()
|
|
||||||
{
|
|
||||||
//Приложения старого мэджик из скрипта Config_AD
|
|
||||||
$oldApps = $this->getOldApps();
|
|
||||||
#Гаврилов
|
|
||||||
//НОВЫЕ ПРИЛОЖЕНИЯ НЕ ИСПОЛЬЗУЕШЬ
|
|
||||||
//Приложения нового мэджик из базы данных
|
|
||||||
#Гаврилов
|
|
||||||
//отрисовывать только то, что может видеть пользователь
|
|
||||||
$newApps = Models\MagicApps::get()->toArray();
|
|
||||||
|
|
||||||
return response()->json($oldApps);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод получает конфиг со старыми приложениями Magic
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getOldApps()
|
|
||||||
{
|
|
||||||
$oldAppConf = json_decode(file_get_contents('http://cs/magic/return_config.AD.php'));
|
|
||||||
|
|
||||||
return $oldAppConf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод должен проверить есть ли избранные приложения у пользователя. Если есть, их необходимо обновить либо добавив, либо убрав то приложение, по которому кликнул пользователь. Если избранных приложений нет - вставить новую запись
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function updateUserFavApp(Request $rqst)
|
|
||||||
{
|
|
||||||
#Гаврилов
|
|
||||||
//ВНИЗУ ИСПОЛЬЗУЕТСЯ ЛОГИН DGAVRILOV, ПЕРЕПИШИ НА ЛОГИН ПОЛЬЗОВАТЕЛЯ MAGIC
|
|
||||||
$userFavApp = $this->getUserFavApp('dgavrilov');
|
|
||||||
$appName = $rqst->all()['appName'];
|
|
||||||
$newFavAppList = null;
|
|
||||||
//Если массив с избранными приложениями пустой - пользователь не добавил ни 1 приложения мэджик в избранное
|
|
||||||
if (!$userFavApp) {
|
|
||||||
$newFavAppList = $appName;
|
|
||||||
} else {
|
|
||||||
$currentFavApp = $userFavApp;
|
|
||||||
if (in_array($appName, $currentFavApp) !== false) {
|
|
||||||
$newFavAppList = implode(';', array_filter($currentFavApp, function($el) use($appName) {return $el !== $appName;}));
|
|
||||||
} else {
|
|
||||||
$newFavAppList = implode(';', array_merge($currentFavApp, [$appName]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($newFavAppList) {
|
|
||||||
#Гаврилов
|
|
||||||
//ПРИ ВЫЗОВЕ ИЗ REACT НЕ ОБНОВЛЯЕТСЯ ПОЛЕ UPDATE_AT
|
|
||||||
$this->insertFavApp($newFavAppList);
|
|
||||||
//Если список приложений пуст - просто удаляем запись с избранными приложениями пользователя
|
|
||||||
} else {
|
|
||||||
$this->delAllUserFavApp('dgavrilov');
|
|
||||||
}
|
|
||||||
return $this->getUserFavApp('dgavrilov');
|
|
||||||
}
|
|
||||||
|
|
||||||
#Гаврилов
|
|
||||||
//try catch для отслеживания ошибок вставки записей в БД?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод вставки новой записи с избранными приложениями
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function insertFavApp($appName)
|
|
||||||
{
|
|
||||||
Models\UserFavApp::updateOrCreate(
|
|
||||||
['user_login' => $this->userLogin],
|
|
||||||
['fav_apps' => $appName]
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод удаляет запись с избранными приложениями пользователя
|
|
||||||
*
|
|
||||||
* @param string $userLogin логин пользователя
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function delAllUserFavApp($userLogin)
|
|
||||||
{
|
|
||||||
Models\UserFavApp::where('user_login', $userLogin)->delete();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод получает избранные приложения пользователя
|
|
||||||
*
|
|
||||||
* @param string $userLogin логин пользователя
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getUserFavApp(string $userLogin): array
|
|
||||||
{
|
|
||||||
#Гаврилов
|
|
||||||
//ЗДЕСЬ ВМЕСТО DGAVRILOV ДОЛЖНО БЫТЬ ОБРАЩЕНИЕ К КУКАСАМ, ЧТОБЫ БРАТЬ ЛОГИН ОТТУДА
|
|
||||||
$userFavApp = Models\UserFavApp::where('user_login', $userLogin)->first();
|
|
||||||
|
|
||||||
return $userFavApp ? explode(';', $userFavApp['fav_apps']) : [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use App\Models\MagicApps;
|
|
||||||
|
|
||||||
class AppRoles extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'app_roles';
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
public function magicApp()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(MagicApps::class, 'app_id', 'id');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use App\Models\AppRoles;
|
|
||||||
|
|
||||||
class MagicApps extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'magic_apps';
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
public function appRoles()
|
|
||||||
{
|
|
||||||
return $this->hasMany(AppRoles::class, 'app_id', 'id')
|
|
||||||
->orderBy('role_priority', 'asc');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class UserFavApp extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
public $incrementing = true;
|
|
||||||
public $timestamps = true;
|
|
||||||
protected $table = 'user_fav_app';
|
|
||||||
protected $connection = "mysql";
|
|
||||||
protected $fillable = ['user_login', 'fav_apps'];
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
-- custom.app_roles definition
|
|
||||||
|
|
||||||
CREATE TABLE `app_roles` (
|
|
||||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
||||||
`app_id` bigint(20) unsigned NOT NULL COMMENT 'id приложения (связь с таблицей magic_apps)',
|
|
||||||
`app_role` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Роль в приложении',
|
|
||||||
`app_title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Название роли на русском',
|
|
||||||
`role_priority` smallint(6) NOT NULL COMMENT 'Приоритет роли',
|
|
||||||
`role_access` text COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Кому доступна роль (зависит от драйвера в magic_apps)',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `app_roles_app_id_foreign` (`app_id`),
|
|
||||||
CONSTRAINT `app_roles_app_id_foreign` FOREIGN KEY (`app_id`) REFERENCES `magic_apps` (`id`)
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Ролевая модель приложений';
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
INSERT INTO custom.app_roles (app_id,app_role,app_title,role_priority,role_access) VALUES
|
|
||||||
(4,'admin','Администратор',1,'# Magic_admins'),
|
|
||||||
(4,'user','Пользователь',2,'# Test');
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
-- custom.magic_apps definition
|
|
||||||
|
|
||||||
CREATE TABLE `magic_apps` (
|
|
||||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
||||||
`app_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Имя приложения',
|
|
||||||
`app_title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Название приложения на русском',
|
|
||||||
`app_link` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`role_driver` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Драйвер определения ролей: по группам AD, по почтовым рассылкам, по логинам. NULL, если ролевая модель не предусмотрена',
|
|
||||||
`access_groups_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Доступ к приложениям по почтовым рассылкам',
|
|
||||||
`access_groups_ad` text COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Доступ к приложениям по группам AD',
|
|
||||||
`is_active` tinyint(4) NOT NULL DEFAULT 1 COMMENT 'Активно ли приложение',
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
INSERT INTO custom.magic_apps (app_name,app_title,app_link,role_driver,access_groups_email,access_groups_ad,is_active) VALUES
|
|
||||||
('Test','','test.index',NULL,NULL,'CTXAL.CryptoPRO',1),
|
|
||||||
('About','','about',NULL,NULL,'CTXAL.CryptoPRO',1),
|
|
||||||
('Test create','','test.create',NULL,NULL,'CTXAL.CryptoPRO',1),
|
|
||||||
('taxi','Реестр заказа такси','test','email','# Magic_admins',NULL,1);
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
-- custom.user_fav_app definition
|
|
||||||
|
|
||||||
CREATE TABLE `user_fav_app` (
|
|
||||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
||||||
`created_at` timestamp NULL DEFAULT NULL,
|
|
||||||
`updated_at` timestamp NULL DEFAULT NULL,
|
|
||||||
`user_login` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`fav_apps` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Избранные приложения пользователя из меню Magic';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
INSERT INTO custom.user_fav_app (created_at,updated_at,user_login,fav_apps) VALUES
|
|
||||||
('2025-03-28 18:16:10','2025-11-14 10:35:04','dgavrilov','reestr_send_app;dadata;amlpor;accext;prlog;anketa;pilotIVI;limcut;nsr;minjust;mcocardreporting;taxi');
|
|
||||||
Binary file not shown.
+9
-187
@@ -1,189 +1,11 @@
|
|||||||
/* ГАВРИЛОВ. ВЫЯСНИТЬ, ГДЕ ИСПОЛЬЗУЮТСЯ */
|
@import 'tailwindcss';
|
||||||
|
|
||||||
html {
|
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||||
overflow-y: scroll;
|
@source '../../storage/framework/views/*.php';
|
||||||
}
|
@source '../**/*.blade.php';
|
||||||
|
@source '../**/*.js';
|
||||||
#root{
|
|
||||||
--color_ruby: #ff0078;
|
@theme {
|
||||||
}
|
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||||
|
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
.container {
|
|
||||||
background-color: lightblue;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu-container{
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&>#menu__left-block{
|
|
||||||
flex-basis: 15%;
|
|
||||||
|
|
||||||
&>.menu__left-block__call-app{
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&>.fa{
|
|
||||||
flex-basis: 20%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.switcher-container{
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&>.switch {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 60px;
|
|
||||||
height: 34px;
|
|
||||||
margin: 9px;
|
|
||||||
|
|
||||||
&>.switcher__favorite-app {
|
|
||||||
opacity: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
|
|
||||||
&.showFav + .slider{
|
|
||||||
background-color: var(--color_ruby);
|
|
||||||
|
|
||||||
&:before{
|
|
||||||
transform: translateX(26px);
|
|
||||||
color: var(--color_ruby);
|
|
||||||
padding: 2px 0 0 0;
|
|
||||||
align-content: baseline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&>.slider {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: #ccc;
|
|
||||||
-webkit-transition: .4s;
|
|
||||||
transition: .4s cubic-bezier(0,1,0.5,1);
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
@import url('https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css');
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
|
||||||
content: "\f006";
|
|
||||||
font-family: FontAwesome;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
height: 26px;
|
|
||||||
width: 26px;
|
|
||||||
left: 4px;
|
|
||||||
bottom: 4px;
|
|
||||||
background-color: white;
|
|
||||||
-webkit-transition: .4s;
|
|
||||||
transition: .4s cubic-bezier(0,1,0.5,1);
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.round {
|
|
||||||
border-radius: 34px;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.switcher__favorite-app.showFav + .slider {
|
|
||||||
background-color: var(--color_ruby);
|
|
||||||
}
|
|
||||||
.switcher__favorite-app.showFav + .slider:before {
|
|
||||||
transform: translateX(26px);
|
|
||||||
}
|
|
||||||
/* Rounded sliders */
|
|
||||||
/* .slider.round {
|
|
||||||
border-radius: 34px;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
#round {
|
|
||||||
border-radius: 34px;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu__app-container__app-block{
|
|
||||||
/* display: grid;
|
|
||||||
grid-template-columns: 50% 50%; */
|
|
||||||
column-count: 2;
|
|
||||||
|
|
||||||
>.apps-block__proc{
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
>.proc__title{
|
|
||||||
font-size: 1.1rem;
|
|
||||||
padding: 10px 0;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
>.proc__title.proc-hide{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.script-list__el{
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
>.script-list__el__script-name{
|
|
||||||
margin-bottom: 5px;
|
|
||||||
transition: 0.2s;
|
|
||||||
&:hover{
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
>a{
|
|
||||||
text-decoration: none;
|
|
||||||
color: black;
|
|
||||||
|
|
||||||
&:hover{
|
|
||||||
color: var(--color_ruby);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
>.fa-star{
|
|
||||||
margin-left: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.2;
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
>.fa-star:hover{
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
>.fa-star.favorite{
|
|
||||||
opacity: 1;
|
|
||||||
color: var(--color_ruby);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.script-list__el.script-hide{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { createRoot } from 'react-dom/client';
|
|
||||||
import '@SharePoint/rencredit_uikit/dist/static/fonts/mont/Mont.css';
|
|
||||||
import '@fortawesome/fontawesome-free/css/all.css';
|
|
||||||
import { UIKitThemeProvider } from '@SharePoint/rencredit_uikit';
|
|
||||||
import MenuApp from './components/MenuApp.tsx'; // Создайте этот файл, если используете React
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const container:HTMLElement = document.getElementById('root')!;
|
|
||||||
const root = createRoot(container);
|
|
||||||
root.render(
|
|
||||||
<UIKitThemeProvider>
|
|
||||||
<MenuApp />
|
|
||||||
</UIKitThemeProvider>
|
|
||||||
);
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
import { Button } from '@SharePoint/rencredit_uikit';
|
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
function MenuApp ()
|
|
||||||
{
|
|
||||||
//Приложения и процессы Magic для отображения в меню
|
|
||||||
const [menuApps, setMenuApps] = useState<{
|
|
||||||
'proccesses': object,
|
|
||||||
'scripts': object
|
|
||||||
}>({
|
|
||||||
'proccesses': {},
|
|
||||||
'scripts': {}
|
|
||||||
});
|
|
||||||
//TODO
|
|
||||||
//ВНЕДРИ В КОНТРОЛЛЕРЕ ПРОВЕРКУ ДОСТУПОВ, А НЕ ВОЗВРАЩАЙ ВСЕ ПОДРЯД ПРИЛОЖЕНИЯ
|
|
||||||
//Избранные приложения пользователя
|
|
||||||
const [favApps, setFavApps] = useState<string[]>([]);
|
|
||||||
//Массив избраннных процессов, в приложениях которых есть хотя бы одно избранное
|
|
||||||
const [favProcs, setFavProcs] = useState<string[]>([]);
|
|
||||||
//Состояния видимости скрипта
|
|
||||||
const [hideScriptClass, setHideScriptClass] = useState('');
|
|
||||||
//const [sanctumToken, setSanctumToken] = useState('');
|
|
||||||
|
|
||||||
const sanctumToken = () => {
|
|
||||||
const metaTag = document.getElementById('sanctum_token_block') as HTMLMetaElement;
|
|
||||||
return metaTag.dataset.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Получаем при рендере страницы все приложения Magic и все избранные приложения пользователя
|
|
||||||
useEffect( () => {
|
|
||||||
Promise.all(
|
|
||||||
[
|
|
||||||
fetch('api/magic_apps').then(menuAppsRes => menuAppsRes.json()),
|
|
||||||
//TODO
|
|
||||||
//СЮДА ПОТЯГИВАЙ ЛОГИН ПОЛЬЗОВАТЕЛЯ, А НЕ DGAVRILOV
|
|
||||||
fetch('api/user_fav_app/dgavrilov').then(favAppsRes => favAppsRes.json())
|
|
||||||
]
|
|
||||||
).then(
|
|
||||||
([
|
|
||||||
responseMenuApp,
|
|
||||||
responseFavApp
|
|
||||||
]) => {
|
|
||||||
setMenuApps(responseMenuApp);
|
|
||||||
setFavApps(responseFavApp);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
//Обновление видимости процесса (собрания приложений) в зависимости от того находится ли в избранном хотя бы одно приложение
|
|
||||||
useEffect( () => {
|
|
||||||
if (menuApps === null) { return }
|
|
||||||
//Массив с процессами, в которых есть хотя бы одно избранное приложение
|
|
||||||
const favProcsArr:string[] = [];
|
|
||||||
//Мы собираем все избранные приложения в массив-состояние, чтобы при изменении состояния каждого приложения (например при переключении switcher) проверять входит ли это приложение в избранные. Если входит - его родительский процесс не прячем, так как в его дочерних приложениях есть избранные
|
|
||||||
Object.entries(menuApps.proccesses).forEach( (procData) => {
|
|
||||||
//Флаг - есть ли в приложениях процесса хотя бы 1 избранное
|
|
||||||
const hasFavorite: boolean = procData[1].tabs.split(';').some( (tab: string) => {
|
|
||||||
return favApps.includes(tab);
|
|
||||||
})
|
|
||||||
if (hideScriptClass !== 'script-hide' || hasFavorite) {
|
|
||||||
favProcsArr.push(procData[0]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setFavProcs(favProcsArr);
|
|
||||||
}, [hideScriptClass, menuApps, favApps]);
|
|
||||||
|
|
||||||
//console.log(sanctumToken())
|
|
||||||
|
|
||||||
//ГАВРИЛОВ
|
|
||||||
//ПОЧЕМУ КОД НИЖЕ ИСПОЛНЯЕТСЯ ПОСТОЯННО ПОКА НЕ БУДЕТ ПОЛУЧЕН КОНТЕНТ ДЛЯ СТРАНИЦЫ ИЗ ПРОМИСОВ ВЫШЕ, А НЕ ОТРАБАТЫВАЕТ ТОЛЬКО ПРИ РЕНДЕРИНГЕ СТРАНИЦЫ
|
|
||||||
//Уместно ли отслеживать состояние до получения запросов fetch в подобном виде?
|
|
||||||
if (menuApps === null || favApps === null) {
|
|
||||||
return <span>прелоадер</span>
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div id = 'menu-container'>
|
|
||||||
<div id = 'menu__left-block'>
|
|
||||||
<div className = 'menu__left-block__call-app'>
|
|
||||||
<i className = 'fa fa-th-large'></i>
|
|
||||||
<div className = 'mleft-block__call-app__title'>Приложения</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id = 'menu__app-container'>
|
|
||||||
<Button text='Текст' />
|
|
||||||
<div id = 'menu__app-container__app-block'>
|
|
||||||
{/* {console.log(menuApps.scripts)} */}
|
|
||||||
{Object.entries(menuApps.proccesses).map( (proc_val, proc_index) => (
|
|
||||||
<div className = "apps-block__proc" key = { proc_index } data-proc = {proc_val[0]}>
|
|
||||||
{/* В зависимости от того входит процесс в массив избранных делаем его видимым или скрываем */}
|
|
||||||
<div className = {`proc__title ${favProcs.includes(proc_val[0]) ? 'proc-visible' : 'proc-hide'}`}>{proc_val[1].title}</div>
|
|
||||||
<div className = "proc__script-list">
|
|
||||||
{proc_val[1].tabs.split(';').map( (app_el, app_index) => (
|
|
||||||
<AppElem
|
|
||||||
appIndex = { app_index }
|
|
||||||
appName = { app_el }
|
|
||||||
appTitle = { menuApps.scripts[app_el]?.title || "Неизвестный скрипт - " + app_el }
|
|
||||||
appUrl = { menuApps.scripts[app_el]?.url }
|
|
||||||
favIconClassName = { favApps.includes(app_el) ? 'favorite' : 'not_favorite' }
|
|
||||||
hideAppClass = { hideScriptClass }
|
|
||||||
//Функция обновления актуальный список избранных приложений
|
|
||||||
setUpdateFavApps = { (newFavApp) => setFavApps(newFavApp) }
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)) }
|
|
||||||
</div>
|
|
||||||
<Switcher
|
|
||||||
toggleAppsVisible = { setHideScriptClass }
|
|
||||||
switcherId = "switcher-menu"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Гаврилов
|
|
||||||
//ПЕРЕПИСАТЬ АРГУМЕНТЫ ПОД ОБЪЕКТ СО СВОЙСТВАМИ, ИСПОЛЬЗОВАТЬ ...obj
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {int} appIndex ключ для экземлпяра компонента
|
|
||||||
* @param {string} appName уникальное имя скрипта
|
|
||||||
* @param {string} appTitle название скрипта в меню
|
|
||||||
* @param {string} favIconClassName класс для иконки избранного
|
|
||||||
* @param {string} hideAppClass класс для видимости приложения
|
|
||||||
* @param {string} setUpdateFavApps функция для обновления состояния избранны приложений (при снятия, установки признака избранности)
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function AppElem({
|
|
||||||
appIndex,
|
|
||||||
appName,
|
|
||||||
appTitle,
|
|
||||||
appUrl,
|
|
||||||
favIconClassName,
|
|
||||||
hideAppClass,
|
|
||||||
setUpdateFavApps
|
|
||||||
} : {
|
|
||||||
appIndex: string,
|
|
||||||
appName: string,
|
|
||||||
appTitle: string,
|
|
||||||
appUrl: string,
|
|
||||||
favIconClassName: string,
|
|
||||||
hideAppClass: string,
|
|
||||||
setUpdateFavApps: string
|
|
||||||
}) {
|
|
||||||
const [appElemClass, changeFav] = useState(favIconClassName);
|
|
||||||
const callChangeFav = ( changeAppName: string ) => {
|
|
||||||
fetch('api/user_fav_app', {
|
|
||||||
method: 'post',
|
|
||||||
body: JSON.stringify({ appName: changeAppName }),
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then( (response) => response.json() )
|
|
||||||
.then( (response) => {
|
|
||||||
//Если скрипт был избранным - удаляем из избранного и наоборот
|
|
||||||
appElemClass == 'favorite' ? changeFav('not_favorite') : changeFav('favorite');
|
|
||||||
//Возвращаем обновленный список избранных приложений пользователя
|
|
||||||
setUpdateFavApps(response?.fav_apps?.split( ';' ) || [])
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
//Если приложение в избранном - оно всегда должно быть "видимо", даже если переключатель в положении Скрыть неизбранные приложения
|
|
||||||
<div key = { appIndex } className = {`script-list__el ${appElemClass == 'favorite' ? '' : hideAppClass}`} data-scriptname = { appName }>
|
|
||||||
<div className = "script-list__el__script-name"><a target="_blank" href={ appUrl }>{ appTitle }</a></div>
|
|
||||||
{/* Анонимная функция нужна для создания функции смены состояния для каждого компонента в отдельности, в противном случае вызов функции в одном экземпляре компонента вызывает функцию изменения для каждого экземпляра компонента */}
|
|
||||||
<i className = {`${(appElemClass == 'favorite' ? 'fas' : 'far')} fa-star ${appElemClass}`} onClick = { () => callChangeFav(appName) }></i>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
//ВЫНЕСИ В ПАПКУ COMPONENTS/MAIN
|
|
||||||
/**
|
|
||||||
* Компонент переключателя В ДАННОМ СЛУЧАЕ видимости скриптов (видны только избранные или все)
|
|
||||||
* @param {string} switcherId идентификатор переключателя (для стилей css), так как в будущем, на странице можно будет размещать несколько переключателей
|
|
||||||
* @param {function} toggleAppsVisible функция обновления состояния hideScriptClass
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function Switcher({ switcherId, toggleAppsVisible }) {
|
|
||||||
const [favSwitcherClass, setFavSwitcherClass] = useState('showAll');
|
|
||||||
|
|
||||||
//Переключение состояния переключателя после рендеринга страницы
|
|
||||||
useEffect( () => {
|
|
||||||
//Получаем из локал сторадж состояние переключателя
|
|
||||||
const savedState = localStorage.getItem('magicMenuFavSwitcher');
|
|
||||||
if (savedState) {
|
|
||||||
setFavSwitcherClass(savedState);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
//При изменении состояния Переключателя изменяется состояние всех приложени (переключается их видимость в зависимости от состояния переключателя - только избранные или все)
|
|
||||||
useEffect( () => {
|
|
||||||
//Синхронизируем зависимые компоненты
|
|
||||||
toggleAppsVisible(favSwitcherClass === 'showFav' ? 'script-hide' : '');
|
|
||||||
}, [favSwitcherClass] );
|
|
||||||
|
|
||||||
const callToggleFavSwitcher = () => {
|
|
||||||
let switcherState = (favSwitcherClass === 'showFav' ? 'showAll' : 'showFav');
|
|
||||||
//favSwitcherClass => favSwitcherClass = ... - функциональное обновление. Здесь оно не особо нужно, но чтобы не забыть
|
|
||||||
//Меняем класс switcher, а также меняем состояние видимости всех приложений (в зависимости от того избранные они или нет)
|
|
||||||
setFavSwitcherClass( favSwitcherClass => {
|
|
||||||
if (favSwitcherClass === 'showFav') {
|
|
||||||
toggleAppsVisible('script-hide')
|
|
||||||
} else {
|
|
||||||
toggleAppsVisible('')
|
|
||||||
}
|
|
||||||
localStorage.setItem('magicMenuFavSwitcher', switcherState)
|
|
||||||
|
|
||||||
return switcherState;
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id = { switcherId } className = "switcher-container">
|
|
||||||
<label className = "switch">
|
|
||||||
<input type = "checkbox" className = {`switcher__favorite-app ${favSwitcherClass}`} onChange = { callToggleFavSwitcher }/>
|
|
||||||
<span className = "slider round"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MenuApp;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Laravel + React</title>
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
||||||
<!-- Без команды ниже корректно не обрабатывается React скрипт -->
|
|
||||||
@viteReactRefresh
|
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.jsx'])
|
|
||||||
<!-- Отрисовываем meta-тег с sanctum-токеном, чтобы на фронте его могли получить и передавать при api -->
|
|
||||||
<meta id="sanctum_token_block" data-token="{{ session('sanctum-token') ?? '' }}"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
|
||||||
use \App\Http\Controllers;
|
|
||||||
|
|
||||||
|
|
||||||
#Гаврилов
|
|
||||||
//НАВЕРНОЕ, НУЖНО ВСЕ ТАКИ СГРУППИРОВАТЬ API РОУТЫ НИЖЕ В ГРУППУ /api/MENU/endpoint
|
|
||||||
/**
|
|
||||||
* Получение всех приложений Magic
|
|
||||||
* todo
|
|
||||||
* возвращать только приложения, доступные согласно роли
|
|
||||||
*/
|
|
||||||
Route::get('magic_apps', [Controllers\MenuController::class, 'getApps']);
|
|
||||||
/**
|
|
||||||
* Получение избранных приложений
|
|
||||||
* {userLogin} - логин пользователя, чьи приложения необходимо вернуть
|
|
||||||
*/
|
|
||||||
Route::get('user_fav_app/{userLogin}', [Controllers\MenuController::class, 'getUserFavApp']);
|
|
||||||
/**
|
|
||||||
* Обновление избранных приложений
|
|
||||||
*/
|
|
||||||
Route::post('user_fav_app', [Controllers\MenuController::class, 'updateUserFavApp']);
|
|
||||||
@@ -5,6 +5,3 @@ use Illuminate\Support\Facades\Route;
|
|||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return view('welcome');
|
return view('welcome');
|
||||||
});
|
});
|
||||||
Route::get('/menu', function () {
|
|
||||||
return view('menu_start');
|
|
||||||
})->name('magic_menu');
|
|
||||||
|
|||||||
Reference in New Issue
Block a user