commit b92ad093f6b62c723e7677bcb488e5822ea7b523 Author: Simon Vieille Date: Wed Mar 24 12:27:07 2021 +0100 init of Murph diff --git a/.env b/.env new file mode 100644 index 0000000..b247ad8 --- /dev/null +++ b/.env @@ -0,0 +1,35 @@ +# In all environments, the following files are loaded if they exist, +# the latter taking precedence over the former: +# +# * .env contains default values for the environment variables needed by the app +# * .env.local uncommitted file with local overrides +# * .env.$APP_ENV committed environment-specific defaults +# * .env.$APP_ENV.local uncommitted environment-specific overrides +# +# Real environment variables win over .env files. +# +# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. +# +# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). +# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration + +###> symfony/framework-bundle ### +APP_ENV=dev +APP_SECRET=e6e287f176fe2c69112fc620e1801bf0 +###< symfony/framework-bundle ### + +###> doctrine/doctrine-bundle ### +# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url +# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml +# +# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" +# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7" +DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8" +###< doctrine/doctrine-bundle ### + +###> symfony/swiftmailer-bundle ### +# For Gmail as a transport, use: "gmail://username:password@localhost" +# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" +# Delivery is disabled by default via "null://localhost" +MAILER_URL=null://localhost +###< symfony/swiftmailer-bundle ### diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..9e7162f --- /dev/null +++ b/.env.test @@ -0,0 +1,6 @@ +# define your env variables for the test env here +KERNEL_CLASS='App\Kernel' +APP_SECRET='$ecretf0rt3st' +SYMFONY_DEPRECATIONS_HELPER=999999 +PANTHER_APP_ENV=panther +PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc07d31 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ + +###> symfony/framework-bundle ### +/.env.local +/.env.local.php +/.env.*.local +/config/secrets/prod/prod.decrypt.private.php +/public/bundles/ +/src/Command/TestCommand.php +/var/ +/vendor/ +###< symfony/framework-bundle ### + +###> symfony/phpunit-bridge ### +.phpunit +.phpunit.result.cache +/phpunit.xml +###< symfony/phpunit-bridge ### + +###> symfony/webpack-encore-bundle ### +/node_modules/ +/public/build/ +npm-debug.log +yarn-error.log +###< symfony/webpack-encore-bundle ### + +/public/uploads/ +!/public/uploads/.gitkeep diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ea624af --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +COMPOSER ?= composer +PHP ?= php7.4 +SSH ?= ssh +WEBPACK ?= webpack +YARN ?= yarn + +all: dep asset clean + +.ONESHELL: +dep: + $(COMPOSER) update --ignore-platform-reqs + $(COMPOSER) install --ignore-platform-reqs + $(YARN) + +asset-watch: + $(WEBPACK) -w + +asset: + $(YARN) + $(WEBPACK) + +clean: + rm -fr var/cache/dev/* + rm -fr var/cache/prod/* + +doctrine-migration: + PHP=$(PHP) ./bin/doctrine-migrate diff --git a/assets/css/admin.scss b/assets/css/admin.scss new file mode 100644 index 0000000..61ecf32 --- /dev/null +++ b/assets/css/admin.scss @@ -0,0 +1,440 @@ +$theme-colors: ( + "primary": #1ab5dc, + "primary-light": lighten(#3183aa, 40%), + "dark-blue": #1e2430, +); + +$grid-gutter-width: 0px; +$pagination-color: #343a40; +$pagination-bg: #ffffff; +$pagination-active-color: #ffffff; +$pagination-active-bg: #343a40; + +@import "~choices.js/src/styles/choices.scss"; +@import "~bootstrap/scss/bootstrap.scss"; +@import "~@fortawesome/fontawesome-free/css/all.css"; + +#logo { + width: 30px; +} + +.choices__list--dropdown { + display: none; +} + +.choices__list--dropdown.is-active { + display: block; +} + +.dropdown-toggle-hide-after { + &::after { + display: none; + } +} + +.login { + &-container { + margin-top: 5%; + margin-bottom: 5%; + } + + &-form { + padding: 5%; + } + + &-image { + width: 100%; + max-width: 80%; + } +} + +.sidebar { + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 100; /* Behind the navbar */ + padding: 71px 0 0; /* Height of navbar */ + box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); +} + +.sidebar-sticky { + position: relative; + top: 0; + height: calc(100vh - 71px); + padding-top: .5rem; + overflow-x: hidden; + overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ +} + +@supports ((position: -webkit-sticky) or (position: sticky)) { + .sidebar-sticky { + position: -webkit-sticky; + position: sticky; + } +} + +.actions-container { + padding-right: 25px; +} + +.thead-light { + a, th { + color: #333333; + } +} + +tr.table-primary-light { + background-color: #ecf5fa; +} + +.td-nowrap { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.bg-dark-blue { + background: #242b3b; + color: #fff; + + .nav-item-label { + color: #fff; + } +} + +.nav-pills { + .nav-item { + margin-right: 3px; + } + + .nav-link:not(.active) { + color: #333; + background: #eee; + } +} + + +.sidebar { + .nav-link { + font-weight: 500; + color: #333; + border-left: 4px solid map-get($theme-colors, 'dark-blue'); + padding-top: 14px; + padding-bottom: 14px; + + .fa { + font-size: 1.2rem; + margin-right: 5px; + min-width: 30px; + color: #fff; + } + + &.active { + font-weight: bold; + border-left: 4px solid map-get($theme-colors, 'primary'); + background: map-get($theme-colors, 'dark-blue'); + } + } + + &-heading { + font-size: .75rem; + text-transform: uppercase; + display: flex; + } + + @media screen and (max-width: 1130px) { + .nav-link { + font-size: 14px; + } + } + + @media screen and (max-width: 770px) { + .nav { + padding-left: 0; + } + + .nav-link { + padding-left: 10px; + } + + .nav-item-label { + display: none; + } + + .sidebar-heading { + display: none; + } + + width: 50px; + } +} + +*[data-selectable-selector] { + -moz-user-select: none; + -webkit-user-select: none; + user-select: none; +} + +*[data-selectable-selector] { + &:hover { + cursor: pointer; + } +} + +.footer { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + z-index: 1000; + height: 35px; + background: #f8f9fa; +} + +.body { + padding-top: 60px; + + .nav { + padding-left: 10px; + } +} + +@media screen and (max-width: 580px) { + .body { + margin-left: 45px; + } + + .sidebar { + width: 50px; + max-width: 100% !important; + + .sidebar-sticky { + width: 50px; + max-width: 100% !important; + } + } +} + +table.table-fixed, .table-fixed > table { + width: 100%; + + tbody { + overflow: auto; + width: 100%; + height: 500px; + } + + thead, tbody, tr, td, th { + display: block; + } + + tbody { + td, th { + float: left; + min-height: 60px; + } + + tr { + clear: left; + } + } + + thead { + tr { + th { + float: left; + + &.sorted { + &::before { + content: '\f0dc'; + font-family: 'FontAwesome'; + color: #aaa; + margin-right: 3px; + } + } + } + } + } +} + +.toast-container { + display: flex; + position: relative; + z-index: 1060; + + .toast-wrapper { + position: fixed; + top: 20px; + right: 20px; + z-index: 1060; + width: 300px; + } +} + +.tab-form { + padding: 15px; +} + +.icon-margin { + margin-right: 4px; +} + +.file-icon { + font-size: 2em; +} + +.d-ib { + display: inline-block; +} + +.list-checkbox { + vertical-align: middle; + margin-right: 10px; + margin-top: -2px; +} + +.password-strenth { + padding: 0 0 5px 0; + margin-top: -4px; + + .col-sm { + height: 8px; + border: 2px solid #fff; + } + + &-info { + font-size: 13px; + height: 22px; + } +} + +.notification-bell:not([disabled]) { + [data-counter]:after { + display: block; + color: #fff; + background: red; + width: 9px; + height: 9px; + position: absolute; + content: ' '; + top: 4px; + right: 10px; + border-radius: 4px; + } +} + +.form-error-icon { + margin-right: 4px; +} + +.custom-file-label::after { + content: "Parcourir"; +} + +#lease_template_html { + height: calc(100vh - 270px); +} + +.panel { + &-toggler { + &:hover { + cursor: pointer; + } + } + + &-content { + display: block; + + &:not(.active) { + display: none; + } + } +} + +*[data-collection-delete-container] { + cursor: pointer; +} + +*[data-collection-add] { + cursor: pointer; +} + +.login-image { + width: 50%; +} + +.tree { + position: relative; + background: white; + color: #212529; + + span { + font-style: italic; + letter-spacing: .4px; + color: #a8a8a8; + } + + .fa-folder-open, .fa-folder { + color: #007bff; + } + + .fa-html5 { + color: #f21f10; + } + + ul { + padding-left: 5px; + list-style: none; + margin: 0; + padding-bottom: 0; + + li { + position: relative; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 15px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + + &:before { + position: absolute; + top: 15px; + left: 0; + width: 10px; + height: 1px; + margin: auto; + content: ''; + background-color: #666; + } + + &:after { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 1px; + height: 100%; + content: ''; + background-color: #666; + } + + &:last-child:after { + height: 15px; + } + } + + a { + cursor: pointer; + + &:hover { + text-decoration: none; + } + } + } +} + +fieldset.form-group { + margin-bottom: 0; +} diff --git a/assets/images/blank.png b/assets/images/blank.png new file mode 100644 index 0000000..5e21c8a Binary files /dev/null and b/assets/images/blank.png differ diff --git a/assets/images/core/logo.svg b/assets/images/core/logo.svg new file mode 100644 index 0000000..0fcfb57 --- /dev/null +++ b/assets/images/core/logo.svg @@ -0,0 +1,92 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..53eae46 Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/no-image.png b/assets/images/no-image.png new file mode 100644 index 0000000..7957221 Binary files /dev/null and b/assets/images/no-image.png differ diff --git a/assets/js/addons/checkbox-checker.js b/assets/js/addons/checkbox-checker.js new file mode 100644 index 0000000..17618fb --- /dev/null +++ b/assets/js/addons/checkbox-checker.js @@ -0,0 +1,31 @@ +const $ = require('jquery'); + +module.exports = function() { + $('*[data-checkbox-ckecker]').click(function() { + const wrapperName = $(this).attr('data-checkbox-ckecker'); + + if (!wrapperName) { + return; + } + + const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]'); + + $(checkboxes).each(function(i, v) { + $(v).prop('checked', true); + }) + }) + + $('*[data-checkbox-unckecker]').click(function() { + const wrapperName = $(this).attr('data-checkbox-unckecker'); + + if (!wrapperName) { + return; + } + + const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]'); + + $(checkboxes).each(function(i, v) { + $(v).prop('checked', false); + }) + }) +}; diff --git a/assets/js/addons/choices.js b/assets/js/addons/choices.js new file mode 100644 index 0000000..76cd778 --- /dev/null +++ b/assets/js/addons/choices.js @@ -0,0 +1,8 @@ +const Choices = require('choices.js'); +const $ = require('jquery'); + +module.exports = function() { + $('*[data-jschoice]').each(function(key, item) { + new Choices(item); + }); +} diff --git a/assets/js/addons/datepicker.js b/assets/js/addons/datepicker.js new file mode 100644 index 0000000..19e77f0 --- /dev/null +++ b/assets/js/addons/datepicker.js @@ -0,0 +1,26 @@ +const Datepicker = require('vanillajs-datepicker') + +const isDateSupported = () => { + const input = document.createElement('input'); + const value = 'a'; + + input.setAttribute('type', 'date'); + input.setAttribute('value', value); + + return input.value !== value; +} + +module.exports = () => { + if (isDateSupported()) { + return + } + + const inputs = document.querySelectorAll('input[type="date"]') + const size = inputs.length + + for (var i = 0, c = inputs.length; i < c; i++) { + new Datepicker.Datepicker(inputs[i], { + format: 'yyyy-mm-dd' + }) + } +} diff --git a/assets/js/addons/dbclick.js b/assets/js/addons/dbclick.js new file mode 100644 index 0000000..f8a6c28 --- /dev/null +++ b/assets/js/addons/dbclick.js @@ -0,0 +1,7 @@ +const $ = require('jquery'); + +module.exports = function() { + $('*[data-dblclick]').dblclick(function(e) { + document.location.href = $(this).attr('data-dblclick'); + }) +}; diff --git a/assets/js/addons/document-selector.js b/assets/js/addons/document-selector.js new file mode 100644 index 0000000..70cbd91 --- /dev/null +++ b/assets/js/addons/document-selector.js @@ -0,0 +1,43 @@ +const $ = require('jquery'); + +let DocumentSelector = () => { + let forms = $('.document-selector-form'); + let btnSubmit = $('#download-archive-form button'); + + let handler = function() { + forms.each((fi, f) => { + let form = $(f); + let ids = form.find('.document-selector-ids'); + let btn = form.find('.document-selector-button'); + + ids.html(''); + let hasSelection = false; + + $('*[data-documents] *[data-selectable-row] input[data-selectable-checkbox]').each((i, c) => { + let checkbox = $(c); + + if (checkbox.is(':checked')) { + ids.append(checkbox[0].outerHTML); + hasSelection = true; + } + }); + + if (hasSelection && btn.length) { + btn.removeAttr('disabled'); + ids.find('input').prop('checked', true); + } else { + btn.attr('disabled', 'disabled'); + } + }) + } + + $('*[data-documents] *[data-selectable-row]').click(function() { + window.setTimeout(handler, 100) + }); + + $('*[data-documents] *[data-selectable-row]').on('clicked', function() { + window.setTimeout(handler, 100) + }); +} + +module.exports = DocumentSelector; diff --git a/assets/js/addons/editor.js b/assets/js/addons/editor.js new file mode 100644 index 0000000..75c06f0 --- /dev/null +++ b/assets/js/addons/editor.js @@ -0,0 +1,23 @@ +module.exports = function() { + if (typeof tinymce === 'undefined') { + return; + } + + tinymce.init({ + selector: '*[data-tinymce]', + base_url: '/vendor/tinymce/', + cache_suffix: '?v=4.1.6', + language: 'fr_FR', + plugins: 'print preview importcss searchreplace visualblocks visualchars fullscreen template table charmap hr pagebreak nonbreaking toc insertdatetime advlist lists wordcount textpattern noneditable help charmap quickbars', + menubar: 'file edit view insert format tools table tc help', + toolbar: 'undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap | fullscreen preview | code', + importcss_append: true, + image_caption: true, + quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable', + noneditable_noneditable_class: "mceNonEditable", + toolbar_drawer: 'sliding', + spellchecker_dialog: true, + tinycomments_mode: 'embedded', + contextmenu: "link image imagetools table configurepermanentpen", + }); +}; diff --git a/assets/js/addons/form-collection.js b/assets/js/addons/form-collection.js new file mode 100644 index 0000000..9e5145c --- /dev/null +++ b/assets/js/addons/form-collection.js @@ -0,0 +1,84 @@ +const $ = require('jquery'); + +const DeleteHandler = (e) => { + e.stopPropagation() + const target = e.target; + let button = $(target); + + if (button.is('[data-collection-delete-container]')) { + button = button.find('*[data-collection-delete]').first() + } + + const id = button.attr('data-collection-delete'); + const collection = button.parents('[data-collection]') + const item = collection.find('*[data-collection-item="' + id + '"]') + + if (confirm('Validez-vous la suppression ?')) { + item.remove(); + collection.trigger('collection.update'); + } +} + +const CollectionInitilizedAndUpdated = (e) => { + const target = $(e.target) + + target.find('*[data-collection-empty]').toggleClass( + 'd-none', + target.find('*[data-collection-item]').length !== 0 + ); + + target.find('*[data-collection-nonempty]').toggleClass( + 'd-none', + target.find('*[data-collection-item]').length === 0 + ); +} + +const FormCollection = () => { + $('*[data-collection]').on( + 'collection.update', + CollectionInitilizedAndUpdated + ); + + $('*[data-collection]').on( + 'collection.init', + CollectionInitilizedAndUpdated + ); + + $('body').on( + 'click', + '*[data-collection-delete], *[data-collection-delete-container]', + DeleteHandler + ); + + $('body').on('click', '*[data-collection-add]', (e) => { + e.stopPropagation() + + const collectionId = $(e.target).attr('data-collection-add') + const collectionContainer = $('*[data-collection="' + collectionId + '"]') + const prototypeContent = $('#' + collectionId).html() + let name = 0 + + collectionContainer.find('*[data-collection-item]').each(function() { + var n = parseInt($(this).attr('data-collection-item')) + + if (n >= name) { + name = n + 1 + } + }) + + collectionContainer.append(prototypeContent) + + const item = collectionContainer.children('*[data-collection-item]:last-child') + const deleteBtn = $('') + + item.find('*[data-collection-delete-container]').first().append(deleteBtn) + item.html(item.html().replace(/__name__/g, name)) + item.attr('data-collection-item', name) + + collectionContainer.trigger('collection.update'); + }); + + $('*[data-collection]').trigger('collection.init'); +} + +module.exports = FormCollection; diff --git a/assets/js/addons/form-confirm.js b/assets/js/addons/form-confirm.js new file mode 100644 index 0000000..4e07e8c --- /dev/null +++ b/assets/js/addons/form-confirm.js @@ -0,0 +1,15 @@ +const $ = require('jquery'); + +module.exports = function() { + $('*[data-form-confirm]').submit(function(e) { + let message = $(this).attr('data-form-confirm'); + + if (!message) { + message = 'Confimez-vous cette action ?'; + } + + if (!confirm(message)) { + e.preventDefault(); + } + }) +}; diff --git a/assets/js/addons/form.js b/assets/js/addons/form.js new file mode 100644 index 0000000..a3e76aa --- /dev/null +++ b/assets/js/addons/form.js @@ -0,0 +1,11 @@ +const $ = require('jquery'); + +module.exports = function() { + $('.custom-file-input').on('change', function(event) { + let inputFile = event.currentTarget; + + $(inputFile).parent() + .find('.custom-file-label') + .html(inputFile.files[0].name); + }); +}; diff --git a/assets/js/addons/modal.js b/assets/js/addons/modal.js new file mode 100644 index 0000000..0e389f2 --- /dev/null +++ b/assets/js/addons/modal.js @@ -0,0 +1,31 @@ +const $ = require('jquery'); + +module.exports = function() { + $('body').on('click', '*[data-modal]', (e) => { + e.preventDefault(); + e.stopPropagation(); + + let container = $('#modal-container'); + + if (!container.length) { + container = $('