Merge pull request #133 from danielbrendel/ui

Merge ui into main
This commit is contained in:
Daniel Brendel
2024-03-05 02:02:51 +01:00
committed by GitHub
30 changed files with 738 additions and 185 deletions
+1
View File
@@ -55,6 +55,7 @@ users have taken. The system features collaborative management, so you can manag
- 🕰️ History feature
- 💬 Group chat
- ⚙️ Profile management
- 🦋 Themes
- 🔑 Admin dashboard
- 📢 Reminders
- 💾 Backups
+1
View File
@@ -87,6 +87,7 @@ return [
array('/admin/location/update', 'POST', 'admin@update_location'),
array('/admin/location/remove', 'ANY', 'admin@remove_location'),
array('/admin/media/logo', 'POST', 'admin@upload_media_logo'),
array('/admin/media/banner', 'POST', 'admin@upload_media_banner'),
array('/admin/media/background', 'POST', 'admin@upload_media_background'),
array('/admin/media/overlay/alpha', 'POST', 'admin@save_overlay_alpha'),
array('/admin/mail/save', 'POST', 'admin@save_mail_settings'),
+6
View File
@@ -53,6 +53,12 @@ class BaseController extends Asatru\Controller\Controller {
UtilsModule::setLanguage($lang);
}
}
$theme = $auth_user->get('theme');
if (($theme) && (is_dir(public_path() . '/themes/' . $theme))) {
ThemeModule::load(public_path() . '/themes/' . $theme);
}
}
}
+24
View File
@@ -275,6 +275,30 @@ class AdminController extends BaseController {
}
}
/**
* Handles URL: /admin/media/banner
*
* @param Asatru\Controller\ControllerArg $request
* @return Asatru\View\RedirectHandler
*/
public function upload_media_banner($request)
{
try {
if ((!isset($_FILES['asset'])) || ($_FILES['asset']['error'] !== UPLOAD_ERR_OK) || ($_FILES['asset']['type'] !== 'image/jpeg')) {
throw new \Exception('Failed to upload file or invalid file uploaded');
}
move_uploaded_file($_FILES['asset']['tmp_name'], public_path() . '/img/banner.jpg');
FlashMessage::setMsg('success', __('app.media_saved'));
return redirect('/admin?tab=media');
} catch (\Exception $e) {
FlashMessage::setMsg('error', $e->getMessage());
return back();
}
}
/**
* Handles URL: /admin/media/background
*
+2 -1
View File
@@ -65,13 +65,14 @@ class UserController extends BaseController {
$name = $request->params()->query('name', null);
$email = $request->params()->query('email', null);
$lang = $request->params()->query('lang', 'en');
$theme = $request->params()->query('theme', 'default');
$chatcolor = $request->params()->query('chatcolor', null);
$show_log = $request->params()->query('show_log', false);
$notify_tasks_overdue = $request->params()->query('notify_tasks_overdue', false);
$notify_tasks_tomorrow = $request->params()->query('notify_tasks_tomorrow', false);
$show_plants_aoru = $request->params()->query('show_plants_aoru', 'added');
UserModel::editPreferences($name, $email, $lang, $chatcolor, $show_log, $notify_tasks_overdue, $notify_tasks_tomorrow, $show_plants_aoru);
UserModel::editPreferences($name, $email, $lang, $theme, $chatcolor, $show_log, $notify_tasks_overdue, $notify_tasks_tomorrow, $show_plants_aoru);
$password = $request->params()->query('password', null);
if ($password) {
+8 -2
View File
@@ -200,7 +200,8 @@ return [
'no_new_version_available' => 'Es wird bereits die neuste Version genutzt.',
'admin_media' => 'Medien',
'media_logo' => 'Arbeitsraum Logo (.png image)',
'media_background' => 'Arbeitsraum Hintergrundbild (.jpg)',
'media_banner' => 'Arbeitsraum Banner (.jpg)',
'media_background' => 'Arbeitsraum Hintergrund (.jpg)',
'media_saved' => 'Das Asset wurde erfolgreich gespeichert',
'enable_history' => 'Historie aktivieren',
'history_name' => 'Name der Historie',
@@ -259,5 +260,10 @@ return [
'import_successful' => 'Erfolgreich importiert!',
'pwa_enable' => 'PWA Unterstützung aktivieren',
'home' => 'Home',
'enable_system_messages' => 'Systemmeldungen aktivieren'
'enable_system_messages' => 'Systemmeldungen aktivieren',
'plant_count' => '{count} Pflanzen',
'danger_count' => '{count} gefährdet',
'all_in_good_standing' => 'Alles Ok',
'theme' => 'Theme',
'themes' => 'Themes'
];
+2 -2
View File
@@ -2,8 +2,8 @@
return [
'added_new_plant' => 'Ich habe eine neue Pflanze hinzugefügt: <a href="{url}">{name}</a>',
'moved_plant_to_history' => 'Ich habe <a href="{url}">{name}</a> zu ' . app('history_name') . ' geschoben',
'restored_plant_from_history' => 'Ich habe <a href="{url}">{name}</a> von ' . app('history_name') . ' wiederhergestellt',
'moved_plant_to_history' => 'Ich habe <a href="{url}">{name}</a> zu {history} geschoben',
'restored_plant_from_history' => 'Ich habe <a href="{url}">{name}</a> von {history} wiederhergestellt',
'deleted_plant' => 'Ich habe <strong>{name}</strong> gelöscht',
'created_task' => 'Ich habe eine neue Aufgabe erstellt: <a href="{url}">{name}</a>',
'completed_task' => 'Ich habe eine Aufgabe fertiggestellt: <a href="{url}">{name}</a>',
+7 -1
View File
@@ -200,6 +200,7 @@ return [
'no_new_version_available' => 'You are using the latest version.',
'admin_media' => 'Media',
'media_logo' => 'Workspace logo (.png image)',
'media_banner' => 'Workspace banner image (.jpg)',
'media_background' => 'Workspace background image (.jpg)',
'media_saved' => 'Media was saved successfully',
'enable_history' => 'Enable history',
@@ -259,5 +260,10 @@ return [
'import_successful' => 'Import succeeded!',
'pwa_enable' => 'Enable PWA support',
'home' => 'Home',
'enable_system_messages' => 'Enable system messages'
'enable_system_messages' => 'Enable system messages',
'plant_count' => '{count} plants',
'danger_count' => '{count} in danger',
'all_in_good_standing' => 'All fine',
'theme' => 'Theme',
'themes' => 'Themes'
];
+1
View File
@@ -38,6 +38,7 @@ class UserModel_Migration {
$this->database->add('lang VARCHAR(512) NULL');
$this->database->add('chatcolor VARCHAR(512) NULL');
$this->database->add('notes TEXT NULL');
$this->database->add('theme VARCHAR(512) NULL');
$this->database->add('show_log BOOLEAN NOT NULL DEFAULT 1');
$this->database->add('show_plants_aoru BOOLEAN NOT NULL DEFAULT 1');
$this->database->add('notify_tasks_overdue BOOLEAN NOT NULL DEFAULT 1');
+30
View File
@@ -575,6 +575,36 @@
}
}
/**
* @param $id
* @return int
* @throws \Exception
*/
public static function getPlantCount($id)
{
try {
return static::raw('SELECT COUNT(*) AS count FROM `' . self::tableName() . '` WHERE location = ? AND history = 0', [$id])->first()->get('count');
} catch (\Exception $e) {
throw $e;
}
}
/**
* @param $id
* @return int
* @throws \Exception
*/
public static function getDangerCount($id)
{
try {
return static::raw('SELECT COUNT(*) AS count FROM `' . self::tableName() . '` WHERE location = ? AND health_state <> ? AND history = 0', [
$id, 'in_good_standing'
])->first()->get('count');
} catch (\Exception $e) {
throw $e;
}
}
/**
* Return the associated table name of the migration
*
+4 -3
View File
@@ -182,6 +182,7 @@ class UserModel extends \Asatru\Database\Model {
* @param $name
* @param $email
* @param $lang
* @param $theme
* @param $chatcolor
* @param $show_log
* @param $notify_tasks_overdue
@@ -190,7 +191,7 @@ class UserModel extends \Asatru\Database\Model {
* @return void
* @throws \Exception
*/
public static function editPreferences($name, $email, $lang, $chatcolor, $show_log, $notify_tasks_overdue, $notify_tasks_tomorrow, $show_plants_aoru)
public static function editPreferences($name, $email, $lang, $theme, $chatcolor, $show_log, $notify_tasks_overdue, $notify_tasks_tomorrow, $show_plants_aoru)
{
try {
$user = static::getAuthUser();
@@ -198,8 +199,8 @@ class UserModel extends \Asatru\Database\Model {
throw new \Exception('User not authenticated');
}
static::raw('UPDATE `' . self::tableName() . '` SET name = ?, email = ?, lang = ?, chatcolor = ?, show_log = ?, notify_tasks_overdue = ?, notify_tasks_tomorrow = ?, show_plants_aoru = ? WHERE id = ?', [
trim($name), trim($email), $lang, $chatcolor, $show_log, $notify_tasks_overdue, $notify_tasks_tomorrow, (int)$show_plants_aoru, $user->get('id')
static::raw('UPDATE `' . self::tableName() . '` SET name = ?, email = ?, lang = ?, theme = ?, chatcolor = ?, show_log = ?, notify_tasks_overdue = ?, notify_tasks_tomorrow = ?, show_plants_aoru = ? WHERE id = ?', [
trim($name), trim($email), $lang, $theme, $chatcolor, $show_log, $notify_tasks_overdue, $notify_tasks_tomorrow, (int)$show_plants_aoru, $user->get('id')
]);
} catch (\Exception $e) {
throw $e;
+122
View File
@@ -0,0 +1,122 @@
<?php
/**
* This class represents your module
*/
class ThemeModule {
/** @var array $theme_data */
public static $theme_data = null;
/**
* @param $path
* @return void
* @throws \Exception
*/
public static function load($path)
{
try {
self::$theme_data = null;
self::$theme_data = json_decode(file_get_contents($path . '/layout.json'));
if (!is_object(self::$theme_data)) {
throw new \Exception('Invalid data @ ' . $path . '/layout.json: ' . print_r(self::$theme_data, true));
}
if (!file_exists($path . '/' . self::$theme_data->banner)) {
throw new \Exception('Banner asset not found');
}
self::$theme_data->banner_url = asset('themes/' . self::$theme_data->name . '/' . self::$theme_data->banner);
self::$theme_data->inline_rules = '';
foreach (self::$theme_data->rules as $key => $value) {
self::$theme_data->inline_rules .= $key . ': ' . $value . ' !important;';
}
if (isset(self::$theme_data->icon)) {
if (!file_exists($path . '/' . self::$theme_data->icon->asset)) {
throw new \Exception('Icon asset not found');
}
self::$theme_data->icon->url = asset('themes/' . self::$theme_data->name . '/' . self::$theme_data->icon->asset);
self::$theme_data->icon->inline_rules = new \stdClass();
self::$theme_data->icon->inline_rules->base = '';
foreach (self::$theme_data->icon->base as $key => $value) {
self::$theme_data->icon->inline_rules->base .= $key . ': ' . $value . ' !important;';
}
self::$theme_data->icon->inline_rules->img = '';
foreach (self::$theme_data->icon->img as $key => $value) {
self::$theme_data->icon->inline_rules->img .= $key . ': ' . $value . ' !important;';
}
}
if (isset(self::$theme_data->accessory)) {
if (!file_exists($path . '/' . self::$theme_data->accessory->asset)) {
throw new \Exception('Icon asset not found');
}
self::$theme_data->accessory->url = asset('themes/' . self::$theme_data->name . '/' . self::$theme_data->accessory->asset);
self::$theme_data->accessory->inline_rules = new \stdClass();
self::$theme_data->accessory->inline_rules->base = '';
foreach (self::$theme_data->accessory->base as $key => $value) {
self::$theme_data->accessory->inline_rules->base .= $key . ': ' . $value . ' !important;';
}
self::$theme_data->accessory->inline_rules->img = '';
foreach (self::$theme_data->accessory->img as $key => $value) {
self::$theme_data->accessory->inline_rules->img .= $key . ': ' . $value . ' !important;';
}
}
} catch (\Exception $e) {
throw $e;
}
}
/**
* @return mixed
* @throws \Exception
*/
public static function data()
{
try {
return self::$theme_data;
} catch (\Exception $e) {
throw $e;
}
}
/**
* @return bool
*/
public static function ready()
{
return (is_object(self::$theme_data));
}
/**
* @return array
* @throws \Exception
*/
public static function list()
{
try {
$result = [];
$folders = scandir(public_path() . '/themes');
foreach ($folders as $folder) {
if (((substr($folder, 0, 1)) !== '.') && (is_dir(public_path() . '/themes/' . $folder))) {
$result[] = $folder;
}
}
return $result;
} catch (\Exception $e) {
throw $e;
}
}
}
+265 -64
View File
@@ -7,7 +7,7 @@ html, body {
height: 100%;
padding: 0;
margin: 0;
background-color: rgb(10, 10, 10);
background-color: rgb(48, 52, 55);
}
body {
@@ -164,11 +164,11 @@ h2 {
}
.form-paragraph-modal a {
color: rgb(5, 67, 183);
color: rgb(52, 105, 215);
}
.form-paragraph-modal a:hover {
color: rgb(5, 67, 183);
color: rgb(52, 105, 215);
text-decoration: underline;
}
@@ -257,10 +257,93 @@ a.navbar-burger:hover {
}
}
.column h1 {
@media screen and (min-width: 768px) {
margin-top: 20px;
}
}
.banner {
width: 100%;
height: 250px;
background-repeat: no-repeat;
background-size: 100% 100%;
@media screen and (max-width: 768px) {
display: none;
}
}
.banner-icon {
position: relative;
top: 210px;
left: 195px;
}
.banner-icon img {
width: 72px;
height: 72px;
}
.banner-accessory {
position: relative;
top: 172px;
right: -83%;
}
.banner-accessory img {
width: 256px;
height: 256px;
}
.content-inner {
position: relative;
padding: 15px;
}
.modal {
z-index: 105;
}
.modal-card {
color: rgb(150, 150, 150);
}
.modal-card-head {
background-color: rgb(55, 59, 62);
border-bottom: 1px solid rgb(59, 59, 59);
}
.modal-card-title {
color: rgb(190, 190, 190);
}
.modal-card-body {
background-color: rgb(48, 52, 55);
}
.modal-card-body label {
color: rgb(150, 150, 150);
}
.modal-card-body input, .modal-card-body select, .modal-card-body textarea, .modal-card-body table {
background-color: rgb(57, 59, 63);
color: rgb(150, 150, 150);
border: 1px solid rgb(100, 100, 100);
}
.modal-card-body input::placeholder, .modal-card-body select::placeholder, .modal-card-body textarea::placeholder {
color: rgb(250, 250, 250);
}
.modal-card-body hr {
background-color: rgb(100, 100, 100);
}
.modal-card-foot {
background-color: rgb(50, 50, 50);
border-top: 1px solid rgb(59, 59, 59);
}
.fade {
transition: opacity 0.65s linear;
}
@@ -336,21 +419,21 @@ fieldset .field {
.location {
position: relative;
display: inline-block;
width: 250px;
height: 230px;
width: 245px;
height: 250px;
@media screen and (max-width: 830px) {
width: 134px;
height: 108px;
height: 137px;
}
margin-left: 10px;
margin-right: 10px;
margin-bottom: 23px;
background-color: rgba(159, 172, 132, 0.2);
border: 1px solid rgb(80, 80, 80);
margin-bottom: 30px;
background-color: rgb(55, 59, 62);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
}
.location:hover {
background-color: rgba(159, 172, 132, 0.35);
box-shadow: 0 0 20px 0 rgba(105, 165, 85, 0.95);
}
.location-title {
@@ -363,7 +446,7 @@ fieldset .field {
padding-bottom: 5px;
}
@media screen and (min-width: 831px) {
padding-bottom: 10px;
padding-bottom: 7px;
}
margin-bottom: 20px;
background-color: rgba(115, 143, 100, 0.9);
@@ -385,6 +468,36 @@ fieldset .field {
}
}
.location-footer {
position: relative;
bottom: -30px;
padding-top: 10px;
padding-left: 10px;
padding-bottom: 10px;
font-size: 0.9em;
text-align: left;
background-color: rgb(50, 54, 59);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
@media screen and (max-width: 830px) {
font-size: 0.75em;
bottom: -15px;
}
}
.location-footer-count-desktop {
display: none;
@media screen and (min-width: 850px) {
display: inherit;
}
}
.location-footer-count-mobile {
display: none;
@media screen and (max-width: 850px) {
display: inherit;
}
}
.upcoming-tasks-overview {
position: relative;
text-align: center;
@@ -407,7 +520,8 @@ fieldset .field {
margin-left: -20px;
margin-right: -20px;
padding-bottom: 5px;
background-color: rgba(150, 150, 150, 0.3);
background-color: rgb(55, 59, 62);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
text-align: center;
}
@@ -484,12 +598,12 @@ fieldset .field {
.plant-card {
position: relative;
display: inline-block;
width: 265px;
width: 245px;
@media screen and (max-width: 552px) {
width: 145px;
height: 257px;
width: 141px;
height: 222px;
}
height: 398px;
height: 375px;
margin-left: 10px;
margin-right: 10px;
margin-bottom: 20px;
@@ -587,6 +701,7 @@ fieldset .field {
display: inline-block;
padding-left: unset;
padding-right: unset;
width: 107% !important;
}
}
@@ -625,11 +740,11 @@ fieldset .field {
float: right;
}
.is-color-yes {
.is-color-yes, .is-color-ok {
color: rgb(115, 214, 103);
}
.is-color-no {
.is-color-no, .is-color-danger {
color: rgb(212, 67, 67)
}
@@ -647,6 +762,7 @@ fieldset .field {
padding: 10px;
color: rgb(200, 200, 200);
background-color: rgba(90, 90, 90, 0.5);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
font-size: 1.0em;
border: 1px solid rgb(100, 100, 100);
border-left: 3px solid rgb(163, 122, 61);
@@ -668,7 +784,7 @@ fieldset .field {
.plant-photo {
position: relative;
width: 345px;
@media screen and (max-width: 390px) {
@media screen and (max-width: 512px) {
width: 100%;
}
height: 543px;
@@ -730,27 +846,52 @@ fieldset .field {
color: rgb(212, 50, 50);
}
.line-up-frames {
@media screen and (min-width: 795px) {
display: flex;
}
}
.warning-plants {
position: relative;
width: 100%;
margin-top: 20px;
flex: 1;
width: 45%;
@media screen and (max-width: 796px) {
flex: unset;
display: inline-block;
width: 100%;
}
margin-top: -10px;
margin-bottom: 45px;
padding: 0 15px 15px 15px;
border: 1px solid rgb(100, 100, 100);
margin-right: 5px;
margin-left: 10px;
padding: 15px 15px 15px 15px;
padding-left: 25px;
padding-bottom: 20px;
@media screen and (max-width: 512px) {
padding-left: 15px;
margin-left: unset;
}
background-color: rgb(55, 59, 62);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.has-warnings {
background-color: rgba(123, 50, 50, 0.5);
color: rgb(200, 105, 105) !important;
}
.is-all-ok {
background-color: rgba(50, 123, 56, 0.5);
//color: rgb(115, 215, 125)
}
.table-bright-color {
background-color: rgb(63, 70, 75);
}
.warning-plants-title {
margin-top: 20px;
margin-bottom: 10px;
margin-top: -2px !important;
margin-bottom: 20px;
font-size: 1.3em;
color: rgb(200, 200, 200);
}
@@ -768,16 +909,24 @@ fieldset .field {
}
.warning-plants-content {
position: relative;
}
.warning-plants-item {
.warning-plants-content table {
width: 100%;
}
.warning-plants-content thead {
display: none;
}
.warning-plants-content td {
color: rgb(150, 150, 150);
margin-bottom: 10px;
}
.warning-plants-item strong {
border: 1px solid rgb(100, 100, 100);
padding-top: 5px;
padding-left: 5px;
padding-right: 5px;
padding-bottom: 5px;
}
.history-years {
@@ -823,24 +972,57 @@ fieldset .field {
.overdue-tasks {
position: relative;
width: 100%;
flex: 1;
width: 45%;
margin-top: -10px;
margin-bottom: 45px;
padding: 0 15px 15px 15px;
background-color: rgba(123, 50, 50, 0.5);
border: 1px solid rgb(100, 100, 100);
margin-left: 5px;
margin-right: 10px;
padding-top: 2px;
padding-left: 30px;
padding-right: 30px;
padding-bottom: 20px;
@media screen and (max-width: 796px) {
flex: unset;
display: inline-block;
width: 100%;
margin-left: unset;
}
@media screen and (max-width: 512px) {
padding-left: 15px;
padding-right: 2px;
}
background-color: rgb(55, 59, 62);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.overdue-tasks-title {
margin-top: 20px;
margin-bottom: 10px;
margin-top: 10px;
margin-bottom: 20px;
font-size: 1.3em;
color: rgb(200, 200, 200);
color: rgb(200, 105, 105);
}
.overdue-tasks-content {
position: relative;
}
.overdue-tasks-content table {
width: 100%;
}
.overdue-tasks-content thead {
display: none;
}
.overdue-tasks-content td {
color: rgb(150, 150, 150);
border: 1px solid rgb(100, 100, 100);
padding-top: 5px;
padding-left: 5px;
padding-right: 5px;
padding-bottom: 5px;
}
.overdue-tasks-item {
@@ -855,7 +1037,8 @@ fieldset .field {
margin-bottom: 45px;
padding: 0 15px 15px 15px;
border: 1px solid rgb(43, 43, 43);
background-color: rgb(0, 0, 0);
background-color: rgb(50, 54, 59);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
@@ -863,7 +1046,7 @@ fieldset .field {
margin-top: 10px;
margin-bottom: 10px;
font-size: 1.3em;
color: rgb(0, 215, 63);
color: rgb(0, 145, 215);
}
.log-content {
@@ -920,7 +1103,8 @@ fieldset .field {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 30px;
background-color: rgba(50, 50, 50, 0.8);
background-color: rgba(73, 79, 83);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
@@ -930,7 +1114,7 @@ fieldset .field {
.plant-gallery-item-header-label {
display: inline-block;
color: rgb(200, 200, 200);
color: rgb(230, 230, 230);
}
.plant-gallery-item-header-action {
@@ -962,7 +1146,7 @@ fieldset .field {
position: absolute;
z-index: 2;
width: 100%;
height: 98%;
height: 97%;
}
.plant-gallery-item-photo-overlay:hover {
@@ -984,7 +1168,7 @@ fieldset .field {
position: relative;
top: -3px;
padding: 10px;
color: rgb(100, 100, 100);
color: rgb(135, 135, 135);
}
.stats {
@@ -997,19 +1181,19 @@ fieldset .field {
.stats-item {
position: relative;
display: inline-block;
width: 194px;
width: 243.9px;
@media screen and (max-width: 512px) {
width: 149px;
width: 135px;
}
height: 135px;
margin-left: 10px;
margin-right: 10px;
height: 130px;
margin-left: 12px;
margin-right: 12px;
margin-bottom: 20px;
padding: 20px;
background-color: rgba(150, 150, 150, 0.3);
border: 1px solid rgb(200, 200, 200);
background-color: rgb(55, 59, 62);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-radius: 4px;
border-left: 3px solid rgb(159, 165, 45);
border-left: 2px solid rgb(159, 165, 45);
}
.stats-item-count {
@@ -1043,7 +1227,7 @@ fieldset .field {
.plant-tags-item {
position: relative;
display: inline-block;
min-width: 90px;
min-width: 125px;
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
@@ -1052,13 +1236,13 @@ fieldset .field {
margin-right: 5px;
margin-bottom: 16px;
text-align: center;
background-color: rgba(200, 200, 200, 0.3);
border: 1px solid rgb(150, 150, 150);
background-color: rgba(123, 123, 123, 0.3);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-radius: 10px;
}
.plant-tags-item:hover {
background-color: rgba(200, 200, 200, 0.5);
background-color: rgba(150, 150, 150, 0.5);
}
.plant-tags-item a {
@@ -1084,7 +1268,7 @@ fieldset .field {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 29px;
background-color: rgba(50, 50, 50, 0.76);
background-color: rgb(55, 59, 62);
border: 1px solid rgb(80, 80, 80);
border-radius: 4px;
}
@@ -1299,12 +1483,20 @@ fieldset .field {
width: 100%;
}
.inventory-groups thead td {
color: rgb(150, 150, 150);
}
.inventory-groups.table td {
border: 1px solid rgb(100, 100, 100);
}
.inventory-groups a {
color: rgb(50, 50, 50);
color: rgb(150, 150, 150);
}
.inventory-groups a:hover {
color: rgb(50, 50, 50);
color: rgb(150, 150, 150);
text-decoration: underline;
}
@@ -1433,6 +1625,7 @@ fieldset .field {
#small-system-messages {
position: fixed;
z-index: 3;
top: 55px;
@media screen and (max-width: 512px) {
width: 100%;
}
@@ -1477,7 +1670,7 @@ fieldset .field {
}
.scroll-to-top-inner {
background-color: rgb(52, 70, 56);
background-color: rgb(80, 80, 80);
border-radius: 50%;
padding: 12px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
@@ -1629,6 +1822,14 @@ fieldset .field {
width: 100%;
}
.preferences-header {
position: relative;
}
.preferences-header i {
color: rgb(150, 150, 150);
}
.admin-tabs {
margin-top: 23px;
}
@@ -1888,7 +2089,7 @@ fieldset .field {
z-index: 100;
padding-left: 5px;
padding-right: 5px;
background-color: rgb(50, 50, 50);
background-color: rgb(30, 30, 30);
@media screen and (min-width: 1087px) {
display: none;
}
+17
View File
@@ -153,6 +153,23 @@
</div>
</form>
<form method="POST" action="{{ url('/admin/media/banner') }}" enctype="multipart/form-data">
@csrf
<div class="field">
<label class="label">{{ __('app.media_banner') }}</label>
<div class="control">
<input type="file" class="input" name="asset" accept=".jpg,.jpeg" required>
</div>
</div>
<div class="field">
<div class="control">
<input type="submit" class="button is-success" value="{{ __('app.save') }}"/>
</div>
</div>
</form>
<form method="POST" action="{{ url('/admin/media/background') }}" enctype="multipart/form-data">
@csrf
+13
View File
@@ -0,0 +1,13 @@
<div class="banner" style="background-image: url('{{ asset('img/banner.jpg') }}');">
@if (file_exists(public_path() . '/img/banner-icon.png'))
<div class="banner-icon">
<img src="{{ asset('img/banner-icon.png') }}" alt="banner-icon"/>
</div>
@endif
@if (file_exists(public_path() . '/img/banner-accessory.png'))
<div class="banner-accessory">
<img src="{{ asset('img/banner-accessory.png') }}" alt="banner-accessory"/>
</div>
@endif
</div>
+71 -63
View File
@@ -1,73 +1,81 @@
<h1>{{ __('app.chat') }}</h1>
<div class="columns">
<div class="column is-1"></div>
<h2 class="smaller-headline">{{ __('app.chat_hint') }}</h2>
<div class="column is-10">
<h1>{{ __('app.chat') }}</h1>
@include('flashmsg.php')
<h2 class="smaller-headline">{{ __('app.chat_hint') }}</h2>
<div class="margin-vertical">
<form id="frmSendChatMessage" method="POST" action="{{ url('/chat/add') }}">
@csrf
@include('flashmsg.php')
<div class="field has-addons">
<div class="control is-stretched">
<input class="input is-input-dark" type="text" name="message" onkeypress="window.vue.handleChatInput();">
</div>
<div class="control">
<a class="button is-success" href="javascript:void(0);" onclick="document.getElementById('frmSendChatMessage').submit();">{{ __('app.send') }}</a>
</div>
<div class="margin-vertical">
<form id="frmSendChatMessage" method="POST" action="{{ url('/chat/add') }}">
@csrf
<div class="field has-addons">
<div class="control is-stretched">
<input class="input is-input-dark" type="text" name="message" onkeypress="window.vue.handleChatInput();">
</div>
<div class="control">
<a class="button is-success" href="javascript:void(0);" onclick="document.getElementById('frmSendChatMessage').submit();">{{ __('app.send') }}</a>
</div>
</div>
</form>
</div>
</form>
</div>
@if (app('chat_showusers', false))
<div class="chat-user-list" id="chat-user-list"></div>
@endif
@if (app('chat_showusers', false))
<div class="chat-user-list" id="chat-user-list"></div>
@endif
<div class="chat" id="chat">
<div class="chat-message chat-typing-indicator">
<div class="chat-message-content">
<span><i id="chat-typing-circle-1" class="fas fa-circle"></i></span>
<span><i id="chat-typing-circle-2" class="fas fa-circle"></i></span>
<span><i id="chat-typing-circle-3" class="fas fa-circle"></i></span>
<div class="chat" id="chat">
<div class="chat-message chat-typing-indicator">
<div class="chat-message-content">
<span><i id="chat-typing-circle-1" class="fas fa-circle"></i></span>
<span><i id="chat-typing-circle-2" class="fas fa-circle"></i></span>
<span><i id="chat-typing-circle-3" class="fas fa-circle"></i></span>
</div>
</div>
@if (isset($messages))
@foreach ($messages as $message)
@if (!$message->get('system'))
<div class="chat-message {{ ($message->get('userId') == $user->get('id')) ? 'chat-message-right' : '' }}">
<div class="chat-message-user">
<div class="is-inline-block" style="color: {{ UserModel::getChatColorForUser($message->get('userId')) }};">{{ UserModel::getNameById($message->get('userId')) }}</div>
@if (ChatViewModel::handleNewMessage($user->get('id'), $message->get('id')))
<div class="chat-message-new">{{ __('app.new') }}</div>
@endif
</div>
<div class="chat-message-content">
<pre>{{ $message->get('message') }}</pre>
</div>
<div class="chat-message-info">
{{ (new Carbon($message->get('created_at')))->diffForHumans() }}
</div>
</div>
@else
<?php $isNewMessage = ChatViewModel::handleNewMessage($user->get('id'), $message->get('id')); ?>
<div class="system-message">
<div class="system-message-left {{ ($isNewMessage) ? 'system-message-left-new' : '' }}">
<div class="system-message-context">{{ UserModel::getNameById($message->get('userId')) . ' @ ' . date('Y-m-d H:s', strtotime($message->get('created_at'))) }}</div>
<div class="system-message-content">{!! $message->get('message') !!}</div>
</div>
@if ($isNewMessage)
<div class="system-message-right">
<div class="system-message-new chat-message-new">{{ __('app.new') }}</div>
</div>
@endif
</div>
@endif
@endforeach
@endif
</div>
</div>
@if (isset($messages))
@foreach ($messages as $message)
@if (!$message->get('system'))
<div class="chat-message {{ ($message->get('userId') == $user->get('id')) ? 'chat-message-right' : '' }}">
<div class="chat-message-user">
<div class="is-inline-block" style="color: {{ UserModel::getChatColorForUser($message->get('userId')) }};">{{ UserModel::getNameById($message->get('userId')) }}</div>
@if (ChatViewModel::handleNewMessage($user->get('id'), $message->get('id')))
<div class="chat-message-new">{{ __('app.new') }}</div>
@endif
</div>
<div class="chat-message-content">
<pre>{{ $message->get('message') }}</pre>
</div>
<div class="chat-message-info">
{{ (new Carbon($message->get('created_at')))->diffForHumans() }}
</div>
</div>
@else
<?php $isNewMessage = ChatViewModel::handleNewMessage($user->get('id'), $message->get('id')); ?>
<div class="system-message">
<div class="system-message-left {{ ($isNewMessage) ? 'system-message-left-new' : '' }}">
<div class="system-message-context">{{ UserModel::getNameById($message->get('userId')) . ' @ ' . date('Y-m-d H:s', strtotime($message->get('created_at'))) }}</div>
<div class="system-message-content">{!! $message->get('message') !!}</div>
</div>
@if ($isNewMessage)
<div class="system-message-right">
<div class="system-message-new chat-message-new">{{ __('app.new') }}</div>
</div>
@endif
</div>
@endif
@endforeach
@endif
</div>
<div class="column is-1"></div>
</div>
+2 -2
View File
@@ -19,7 +19,7 @@
@endif
<div class="columns plant-column">
<div class="column is-half">
<div class="column is-two-third">
<table>
<thead>
<tr>
@@ -124,7 +124,7 @@
</table>
</div>
<div class="column is-half">
<div class="column is-one-third">
<div class="plant-photo" style="background-image: url('{{ asset('img/' . $plant->get('photo')) }}');">
<div class="plant-photo-overlay">
<div class="plant-photo-view is-pointer" onclick="window.vue.showImagePreview('{{ str_replace('_thumb', '', asset('img/' . $plant->get('photo'))) }}');"><i class="fas fa-expand fa-lg"></i></div>
+86 -22
View File
@@ -24,33 +24,69 @@
</div>
</div>
@if (count($warning_plants) > 0)
<div class="warning-plants has-warnings">
<div class="warning-plants-title">{{ __('app.warning_plants_title') }}</div>
<div class="line-up-frames">
@if (count($warning_plants) > 0)
<div class="warning-plants has-warnings">
<div class="warning-plants-title has-warnings">{{ __('app.warning_plants_title') }}</div>
<div class="warning-plants-content">
@foreach ($warning_plants as $plant)
<div class="warning-plants-item">{{ $plant->get('name') }} | <strong class="plant-state-{{ $plant->get('health_state') }}">{{ __('app.' . $plant->get('health_state')) }}</strong> | {{ (new Carbon($plant->get('last_edited_date')))->diffForHumans() }} | <a class="is-yellow-link" href="{{ url('/plants/details/' . $plant->get('id')) }}">{{ __('app.view_plant_details') }}</a></div>
@endforeach
<div class="warning-plants-content">
<table>
<thead>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</thead>
<tbody>
<?php $table_counter = 0; ?>
@foreach ($warning_plants as $plant)
<tr class="{{ ($table_counter % 2 === 0) ? 'table-bright-color' : '' }}">
<td><a class="is-yellow-link" href="{{ url('/plants/details/' . $plant->get('id')) }}">{{ (strlen($plant->get('name')) > 20) ? substr($plant->get('name'), 0, 20) . '...' : $plant->get('name') }}</a></td>
<td><strong class="plant-state-{{ $plant->get('health_state') }}">{{ __('app.' . $plant->get('health_state')) }}</strong></td>
<td>{{ (new Carbon($plant->get('last_edited_date')))->diffForHumans() }}</td>
</tr>
<?php $table_counter++; ?>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@else
<div class="warning-plants is-all-ok">
<div class="warning-plants-title warning-plants-title-margin-top-25 warning-plants-title-centered"><i class="far fa-check-circle is-color-yes"></i>&nbsp;{{ __('app.warning_plants_all_ok') }}</div>
</div>
@endif
@else
<div class="warning-plants is-all-ok">
<div class="warning-plants-title warning-plants-title-margin-top-25 warning-plants-title-centered"><i class="far fa-check-circle is-color-yes"></i>&nbsp;{{ __('app.warning_plants_all_ok') }}</div>
</div>
@endif
@if (count($overdue_tasks) > 0)
<div class="overdue-tasks">
<div class="overdue-tasks-title">{{ __('app.overdue_tasks') }}</div>
@if (count($overdue_tasks) > 0)
<div class="overdue-tasks">
<div class="overdue-tasks-title">{{ __('app.overdue_tasks') }}</div>
<div class="overdue-tasks-content">
@foreach ($overdue_tasks as $overdue_task)
<div class="overdue-tasks-item">{{ $overdue_task->get('title') }} | {{ date('Y-m-d', strtotime($overdue_task->get('due_date'))) }} | <a class="is-yellow-link" href="{{ url('/tasks#task-anchor-' . $overdue_task->get('id')) }}">{{ __('app.view_task_details') }}</a></div>
@endforeach
</div>
<div class="overdue-tasks-content">
<table>
<thead>
<tr>
<td></td>
<td></td>
</tr>
</thead>
<tbody>
<?php $table_counter = 0; ?>
@foreach ($overdue_tasks as $overdue_task)
<tr class="{{ ($table_counter % 2 === 0) ? 'table-bright-color' : '' }}">
<td><a class="is-yellow-link" href="{{ url('/tasks#task-anchor-' . $overdue_task->get('id')) }}">{{ $overdue_task->get('title') }}</a></td>
<td>{{ date('Y-m-d', strtotime($overdue_task->get('due_date'))) }}</td>
</tr>
<?php $table_counter++; ?>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
</div>
@endif
<div class="locations">
@foreach ($locations as $location)
@@ -63,6 +99,34 @@
<div class="location-icon">
<i class="{{ $location->get('icon') }}"></i>
</div>
<div class="location-footer">
<div class="is-inline-block">
<?php $plant_count = PlantsModel::getPlantCount($location->get('id')); ?>
<span class="location-footer-count-desktop"><i class="fas fa-seedling is-color-ok"></i>&nbsp;{{ __('app.plant_count', ['count' => $plant_count]) }} &nbsp;</span>
<span class="location-footer-count-mobile"><i class="fas fa-seedling is-color-ok"></i>&nbsp;{{ $plant_count }} &nbsp;</span>
</div>
<div class="is-inline-block">
<?php $danger_count = PlantsModel::getDangerCount($location->get('id')); ?>
<span class="location-footer-count-desktop">
@if ($danger_count > 0)
<i class="fas fa-biohazard is-color-danger"></i>&nbsp;{{ __('app.danger_count', ['count' => $danger_count]) }}
@else
<i class="far fa-check-circle is-color-ok"></i>&nbsp;{{ __('app.all_in_good_standing') }}
@endif
</span>
<span class="location-footer-count-mobile">
@if ($danger_count > 0)
<i class="fas fa-biohazard is-color-danger"></i>&nbsp;{{ $danger_count }}
@else
<i class="far fa-check-circle is-color-ok"></i>&nbsp;{{ __('app.all_in_good_standing') }}
@endif
</span>
</div>
</div>
</div>
</a>
@endforeach
+21 -4
View File
@@ -25,19 +25,25 @@
@include('navbar.php')
@if (ThemeModule::ready())
@include('theme.php')
@else
@include('banner.php')
@endif
<div id="small-system-messages"></div>
<div class="container">
<div class="columns">
<div class="column is-2"></div>
<div class="column is-1"></div>
<div class="column is-8 is-image-container" style="background-image: url('{{ asset('img/background.jpg') }}');">
<div class="column-overlay" {!! ((app('overlay_alpha')) ? 'style="background-color: rgba(0, 0, 0, ' . app('overlay_alpha') . ');"': '') !!}>
<div class="column is-10">
<div class="content-inner">
{%content%}
</div>
</div>
<div class="column is-2"></div>
<div class="column is-1"></div>
</div>
</div>
@@ -642,6 +648,17 @@
</div>
</div>
<div class="field">
<label class="label">{{ __('app.theme') }}</label>
<div class="control">
<select class="input" name="theme" id="selEditCombo">
@foreach (ThemeModule::list() as $theme)
<option value="{{ $theme }}" {{ ($user->get('theme') === $theme) ? 'selected' : ''}}>{{ $theme }}</option>
@endforeach
</select>
</div>
</div>
<div class="field {{ ((!app('chat_enable')) ? 'is-hidden': '') }}">
<label class="label">{{ __('app.chatcolor') }}</label>
<div class="control">
+9 -5
View File
@@ -1,13 +1,17 @@
<h1>{{ __('app.profile') }}</h1>
<div class="preferences-header">
<div class="is-inline-block">
<h1>{{ __('app.profile') }}</h1>
</div>
<div class="is-inline-block" title="{{ __('app.preferences') }}">
<a href="javascript:void(0);" onclick="window.vue.bShowEditPreferences = true;">&nbsp;&nbsp;<i class="fas fa-cog fa-2x"></i></a>
</div>
</div>
<div class="margin-vertical is-default-text-color">{{ __('app.profile_hint', ['name' => $user->get('name'), 'email' => $user->get('email')]) }}</div>
@include('flashmsg.php')
<div class="margin-vertical">
<a class="button is-link" href="javascript:void(0);" onclick="window.vue.bShowEditPreferences = true;">{{ __('app.preferences') }}</a>
</div>
<div class="margin-vertical">
<h2 class="smaller-headline">{{ __('app.personal_notes') }}</h2>
+13
View File
@@ -0,0 +1,13 @@
<div class="banner" style="background-image: url('{{ ThemeModule::data()->banner_url }}'); {{ ThemeModule::data()->inline_rules }}">
@if (isset(ThemeModule::data()->icon))
<div class="banner-icon" style="{{ ThemeModule::data()->icon->inline_rules->base }}">
<img src="{{ ThemeModule::data()->icon->url }}" alt="banner-icon" style="{{ ThemeModule::data()->icon->inline_rules->img }}"/>
</div>
@endif
@if (isset(ThemeModule::data()->accessory))
<div class="banner-accessory" style="{{ ThemeModule::data()->accessory->inline_rules->base }}">
<img src="{{ ThemeModule::data()->accessory->url }}" alt="banner-accessory" style="{{ ThemeModule::data()->accessory->inline_rules->img }}"/>
</div>
@endif
</div>
+2
View File
@@ -1,6 +1,8 @@
*
!.gitignore
!background.jpg
!banner.jpg
!banner-icon.png
!placeholder.jpg
!partypopper.png
!plantsempty.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -15
View File
@@ -1,15 +1 @@
{
"name": "HortusFox",
"short_name": "HortusFox",
"icons": [
{
"src": "/logo.png",
"sizes": "256x256",
"type": "image/png"
}
],
"start_url": "/",
"display": "fullscreen",
"background_color": "white",
"theme_color": "white"
}
{"name":"Gartenwahnsinn","short_name":"Gartenwahnsinn","icons":[{"src":"\/logo.png","sizes":"256x256","type":"image\/png"}],"start_url":"\/","display":"fullscreen","background_color":"white","theme_color":"white"}
Binary file not shown.

After

Width:  |  Height:  |  Size: 837 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

+29
View File
@@ -0,0 +1,29 @@
{
"name": "default",
"banner": "banner.jpg",
"rules": {
"height": "250px"
},
"icon": {
"asset": "banner-icon.png",
"base": {
"top": "210px",
"left": "195px"
},
"img": {
"width": "72px",
"height": "72px"
}
},
"accessory": {
"asset": "banner-accessory.png",
"base": {
"top": "172px",
"right": "-83%"
},
"img": {
"width": "256px",
"height": "256px"
}
}
}