init dev-project

This commit is contained in:
kieran@4bis.nl 2024-04-15 17:57:13 +02:00
commit 77f6bb2061
92 changed files with 22305 additions and 0 deletions

40
.env Normal file
View File

@ -0,0 +1,40 @@
# 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.
# https://symfony.com/doc/current/configuration/secrets.html
#
# 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=ab892a986d68cc476c3bcca606175c22
###< 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://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ###
###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
###< symfony/messenger ###
###> symfony/mailer ###
# MAILER_DSN=null://null
###< symfony/mailer ###

6
.env.test Normal file
View File

@ -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

34
.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
###> symfony/framework-bundle ###
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###
### php-storm
.idea
/.idea
###> phpunit/phpunit ###
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###
/.idea
.idea
###> symfony/phpunit-bridge ###
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> liip/imagine-bundle ###
/public/media/cache/
###< liip/imagine-bundle ###
=======
###> symfony/webpack-encore-bundle ###
/node_modules/
npm-debug.log
yarn-error.log
###< symfony/webpack-encore-bundle ###
.env.local
bin/5s-export

8
assets/app.js Normal file
View File

@ -0,0 +1,8 @@
import {registerReactControllerComponents} from '@symfony/ux-react';
registerReactControllerComponents(require.context('./react/controllers', true, /\.(j|t)sx?$/));
import './styles/app.scss';
import * as bootstrap from 'bootstrap';
import './bootstrap'

7
assets/bootstrap.js vendored Normal file
View File

@ -0,0 +1,7 @@
import { startStimulusApp } from '@symfony/stimulus-bridge';
export const app = startStimulusApp(require.context(
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
true,
/\.(j|t)sx?$/
));

17
assets/controllers.json Normal file
View File

@ -0,0 +1,17 @@
{
"controllers": {
"@symfony/ux-chartjs": {
"chart": {
"enabled": true,
"fetch": "eager"
}
},
"@symfony/ux-react": {
"react": {
"enabled": true,
"fetch": "eager"
}
}
},
"entrypoints": []
}

View File

@ -0,0 +1,16 @@
import { Controller } from '@hotwired/stimulus';
/*
* This is an example Stimulus controller!
*
* Any element with a data-controller="hello" attribute will cause
* this controller to be executed. The name "hello" comes from the filename:
* hello_controller.js -> "hello"
*
* Delete this file or adapt it for your use!
*/
export default class extends Controller {
connect() {
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
}
}

View File

@ -0,0 +1,52 @@
.btn-circle {
border-radius: 100%;
height: 2.5rem;
width: 2.5rem;
font-size: 1rem;
display: inline-flex;
align-items: center;
justify-content: center;
&.btn-sm {
height: 1.8rem;
width: 1.8rem;
font-size: 0.75rem;
}
&.btn-lg {
height: 3.5rem;
width: 3.5rem;
font-size: 1.35rem;
}
}
.btn-icon-split {
padding: 0;
overflow: hidden;
display: inline-flex;
align-items: stretch;
justify-content: center;
.icon {
background: fade-out($black, .85);
display: inline-block;
padding: $btn-padding-y $btn-padding-x;
}
.text {
display: inline-block;
padding: $btn-padding-y $btn-padding-x;
}
&.btn-sm {
.icon {
padding: $btn-padding-y-sm $btn-padding-x-sm;
}
.text {
padding: $btn-padding-y-sm $btn-padding-x-sm;
}
}
&.btn-lg {
.icon {
padding: $btn-padding-y-lg $btn-padding-x-lg;
}
.text {
padding: $btn-padding-y-lg $btn-padding-x-lg;
}
}
}

View File

@ -0,0 +1,36 @@
// Custom Card Styling
.card {
.card-header {
// Format Dropdowns in Card Headings
.dropdown {
line-height: 1;
.dropdown-menu {
line-height: 1.5;
}
}
}
// Collapsable Card Styling
.card-header[data-toggle="collapse"] {
text-decoration: none;
position: relative;
padding: 0.75rem 3.25rem 0.75rem 1.25rem;
&::after {
position: absolute;
right: 0;
top: 0;
padding-right: 1.725rem;
line-height: 51px;
font-weight: 900;
content: '\f107';
font-family: 'Font Awesome 5 Free';
color: $gray-400;
}
&.collapsed {
border-radius: $card-border-radius;
&::after {
content: '\f105';
}
}
}
}

View File

@ -0,0 +1,29 @@
// Area Chart
.chart-area {
position: relative;
height: 10rem;
width: 100%;
@include media-breakpoint-up(md) {
height: 20rem;
}
}
// Bar Chart
.chart-bar {
position: relative;
height: 10rem;
width: 100%;
@include media-breakpoint-up(md) {
height: 20rem;
}
}
// Pie Chart
.chart-pie {
position: relative;
height: 15rem;
width: 100%;
@include media-breakpoint-up(md) {
height: calc(20rem - 43px) !important;
}
}

View File

@ -0,0 +1,21 @@
// Custom Dropdown Styling
.dropdown {
.dropdown-menu {
font-size: $dropdown-font-size;
.dropdown-header {
@extend .text-uppercase;
font-weight: 800;
font-size: 0.65rem;
color: $gray-500;
}
}
}
// Utility class to hide arrow from dropdown
.dropdown.no-arrow {
.dropdown-toggle::after {
display: none;
}
}

View File

@ -0,0 +1,53 @@
@use "sass:math";
// Lucas Bebber's Glitch Effect
// Tutorial and CSS from CSS Tricks
// https://css-tricks.com/glitch-effect-text-images-svg/
.error {
color: $gray-800;
font-size: 7rem;
position: relative;
line-height: 1;
width: 12.5rem;
}
@keyframes noise-anim {
$steps: 20;
@for $i from 0 through $steps {
#{percentage($i*math.div(1, $steps))} {
clip: rect(random(100)+px,9999px,random(100)+px,0);
}
}
}
.error:after {
content: attr(data-text);
position: absolute;
left: 2px;
text-shadow: -1px 0 $red;
top: 0;
color: $gray-800;
background: $gray-100;
overflow: hidden;
clip: rect(0,900px,0,0);
animation: noise-anim 2s infinite linear alternate-reverse;
}
@keyframes noise-anim-2 {
$steps: 20;
@for $i from 0 through $steps {
#{percentage($i*math.div(1, $steps))} {
clip: rect(random(100)+px,9999px,random(100)+px,0);
}
}
}
.error:before {
content: attr(data-text);
position: absolute;
left: -2px;
text-shadow: 1px 0 $blue;
top: 0;
color: $gray-800;
background: $gray-100;
overflow: hidden;
clip: rect(0,900px,0,0);
animation: noise-anim-2 3s infinite linear alternate-reverse;
}

View File

@ -0,0 +1,14 @@
footer.sticky-footer {
padding: 2rem 0;
flex-shrink: 0;
.copyright {
line-height: 1;
font-size: 0.8rem;
}
}
body.sidebar-toggled {
footer.sticky-footer {
width: 100%;
}
}

View File

@ -0,0 +1,287 @@
// Global component styles
html {
position: relative;
min-height: 100%;
}
body {
height: 100%;
}
a {
&:focus {
outline: none;
}
}
.homeBlock i{
font-size: 100px;
color: #3F9464;
opacity: 0.5;
}
.homeBlock i:hover{
opacity: 1;
font-size: 110px;
}
.homeBlock span{
font-weight: bold;
}
.showWhenDone {
display: none;
}
.row.ip.deleted {
background-color: rgba(234, 14, 14, 0.35);
}
.row.ip.whitelisted {
background-color: rgba(63, 191, 63, 0.35);
}
.queueRow.whitelisted {
background-color: rgba(63, 191, 63, 0.35);
}
.whitelisted{
background-color: rgba(63, 191, 63, 0.35);
}
/** FourbisButton **/
.fourbisButton {
width: 100%;
color: white;
font-weight: bold;
background-color: #4CB078;
opacity: 0.5;
}
.fourbisButton:hover {
opacity: 1;
}
.fourbisButton:disabled {
background-image: linear-gradient(45deg, #4cb078 15%, #58d18e 15%, #58d18e 50%, #4cb078 50%, #4cb078 65%, #58d18e 65%, #58d18e 100%);
background-size: 28.28px 28.28px;
}
.fourbisButton:disabled:hover {
opacity: 0.5;
background-image: linear-gradient(45deg, #4cb078 15%, #58d18e 15%, #58d18e 50%, #4cb078 50%, #4cb078 65%, #58d18e 65%, #58d18e 100%);
background-size: 28.28px 28.28px;
}
/** FourbisButtonDanger **/
.fourbisButtonDanger {
width: 100%;
color: white;
font-weight: bold;
background-color: #ff0000;
opacity: 0.5;
}
.fourbisButtonDanger:hover {
opacity: 1;
}
.fourbisButtonDanger:disabled {
background-image: linear-gradient(45deg, #ff0000 15%, #c20808 15%, #c20808 50%, #ff0000 50%, #ff0000 65%, #c20808 65%, #c20808 100%);
background-size: 28.28px 28.28px;
}
.fourbisButtonDanger:disabled:hover {
opacity: 0.5;
background-image: linear-gradient(45deg, #ff0000 15%, #c20808 15%, #c20808 50%, #ff0000 50%, #ff0000 65%, #c20808 65%, #c20808 100%);
background-size: 28.28px 28.28px;
}
/** FourbisButtonRefresh **/
.fourbisButtonRefresh {
width: 100%;
color: white;
font-weight: bold;
background-color: #0094C7;
opacity: 0.5;
}
.fourbisButtonRefresh:hover {
opacity: 1;
}
.fourbisButtonRefresh:disabled {
background-image: linear-gradient(45deg, #0094c7 25%, #007ba8 25%, #007ba8 50%, #0094c7 50%, #0094c7 75%, #007ba8 75%, #007ba8 100%);
background-size: 28.28px 28.28px;
}
.fourbisButtonRefresh:disabled:hover {
opacity: 0.5;
background-image: linear-gradient(45deg, #0094c7 25%, #007ba8 25%, #007ba8 50%, #0094c7 50%, #0094c7 75%, #007ba8 75%, #007ba8 100%);
background-size: 28.28px 28.28px;
}
.portalTitle {
color: $primary;
font-size: xx-large;
font-weight: bold;
}
.loadingDiv {
align-content: center;
text-align: center;
height: 70px;
background-color: #8fd8ad;
border-left: 2px solid #4CB07A;
border-right: 2px solid #4CB078;
}
.fourbisContainer {
border: 2px solid #4CB078;
background-color: white;
margin-top: 20px;
padding: 10px;
}
.ContentBlock {
display: inline-block;
border: 1px solid black;
text-align: center;
background-color: #4CB078;
height: 300px;
transition: 0.3s;
}
.ContentBlock:hover {
transform: scale(1.1);
cursor: pointer;
}
.ContentBlock h3 {
font-weight: bold;
}
#fail2banDashboard {
padding-left: 17px;
list-style-type: decimal;
}
#loading {
align-content: center;
text-align: center;
}
/** LOADER **/
.lds-ellipsis {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ellipsis div {
position: absolute;
top: 33px;
width: 13px;
height: 13px;
border-radius: 50%;
background: #419766;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
.lds-ellipsis div:nth-child(1) {
left: 8px;
animation: lds-ellipsis1 0.6s infinite;
}
.lds-ellipsis div:nth-child(2) {
left: 8px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(3) {
left: 32px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(4) {
left: 56px;
animation: lds-ellipsis3 0.6s infinite;
}
@keyframes lds-ellipsis1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes lds-ellipsis3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes lds-ellipsis2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(24px, 0);
}
}
// Main page wrapper
#wrapper {
display: flex;
#content-wrapper {
background-color: #E5E5E5;
width: 100%;
overflow-x: hidden;
#content {
flex: 1 0 auto;
}
}
}
// Set container padding to match gutter width instead of default 15px
.container,
.container-fluid {
padding-left: $grid-gutter-width;
padding-right: $grid-gutter-width;
}
// Scroll to top button
.scroll-to-top {
position: fixed;
right: 1rem;
bottom: 1rem;
display: none;
width: 2.75rem;
height: 2.75rem;
text-align: center;
color: $white;
background: fade-out($gray-800, .5);
line-height: 46px;
&:focus,
&:hover {
color: white;
}
&:hover {
background: $gray-800;
}
i {
font-weight: 800;
}
}

View File

@ -0,0 +1,51 @@
// Pulling these images from Unsplash
// Toshi the dog from https://unsplash.com/@charlesdeluvio - what a funny dog...
.bg-login-image {
background: url("/assets/images/login-image.jpg");
background-position: center;
background-size: cover;
//transform: rotateY(180deg);
}
.bg-register-image {
background: url('https://source.unsplash.com/Mv9hjnEUHR4/600x800');
background-position: center;
background-size: cover;
}
.bg-password-image {
background: url('https://source.unsplash.com/oWTW-jNGl9I/600x800');
background-position: center;
background-size: cover;
}
form.user {
.custom-checkbox.small {
label {
line-height: 1.5rem;
}
}
.form-control-user {
font-size: 0.8rem;
border-radius: 10rem;
padding: 1.5rem 1rem;
}
.btn-user {
font-size: 0.8rem;
border-radius: 10rem;
padding: 0.75rem 1rem;
}
}
.btn-google {
@include button-variant($brand-google, $white);
}
.btn-facebook {
@include button-variant($brand-facebook, $white);
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,3 @@
@import "navs/global.scss";
@import "navs/topbar.scss";
@import "navs/sidebar.scss";

View File

@ -0,0 +1,7 @@
@import "utilities/animation.scss";
@import "utilities/background.scss";
@import "utilities/display.scss";
@import "utilities/text.scss";
@import "utilities/border.scss";
@import "utilities/progress.scss";
@import "utilities/rotate.scss";

View File

@ -0,0 +1,76 @@
// Override Bootstrap default variables here
// Do not edit any of the files in /vendor/bootstrap/scss/!
// Color Variables
// Bootstrap Color Overrides
$white: #fff !default;
$gray-100: #d9d9d9 !default;
$gray-200: #eaecf4 !default;
$gray-300: #dddfeb !default;
$gray-400: #d1d3e2 !default;
$gray-500: #b7b9cc !default;
$gray-600: #858796 !default;
$gray-700: #6e707e !default;
$gray-800: #5a5c69 !default;
$gray-900: #3a3b45 !default;
$black: #000 !default;
$blue: #4e73df !default;
$indigo: #6610f2 !default;
$purple: #6f42c1 !default;
$pink: #e83e8c !default;
$red: #e74a3b !default;
$orange: #fd7e14 !default;
$yellow: #f6c23e !default;
$green: #1cc88a !default;
$teal: #20c9a6 !default;
$cyan: #36b9cc !default;
// Custom Colors
$brand-google: #ea4335;
$brand-facebook: #3b5998;
// Set Contrast Threshold
$yiq-contrasted-threshold: 195 !default;
// Typography
$body-color: $gray-600 !default;
$font-family-sans-serif: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", 'Noto Color Emoji' !default;
$font-weight-light: 300 !default;
// $font-weight-base: 400;
$headings-font-weight: 400 !default;
// Shadows
$box-shadow-sm: 0 0.125rem 0.25rem 0 rgba($gray-900, .2) !default;
$box-shadow: 0 0.15rem 1.75rem 0 rgba($gray-900, .15) !default;
// $box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;
// Borders Radius
$border-radius: 0.35rem !default;
$border-color: darken($gray-200, 2%);
// Spacing Variables
// Change below variable if the height of the navbar changes
$topbar-base-height: 50px;
// Change below variable to change the width of the sidenav
$sidebar-base-width: 14rem;
// Change below variable to change the width of the sidenav when collapsed
$sidebar-collapsed-width: 6.5rem;
// Card
$card-cap-bg: $gray-100;
$card-border-color: $border-color;
// Adjust column spacing for symmetry
$spacer: 1rem;
$grid-gutter-width: $spacer * 1.5;
// Transitions
$transition-collapse: height .15s ease !default;
// Dropdowns
$dropdown-font-size: 0.85rem;
$dropdown-border-color: $border-color;

View File

@ -0,0 +1,41 @@
// Global styles for both custom sidebar and topbar compoments
.sidebar,
.topbar {
.nav-item {
// Customize Dropdown Arrows for Navbar
&.dropdown {
.dropdown-toggle {
&::after {
width: 1rem;
text-align: center;
float: right;
vertical-align: 0;
border: 0;
font-weight: 900;
content: '\f105';
font-family: 'Font Awesome 5 Free';
}
}
&.show {
.dropdown-toggle::after {
content: '\f107';
}
}
}
// Counter for nav links and nav link image sizing
.nav-link {
position: relative;
.badge-counter {
position: absolute;
transform: scale(0.7);
transform-origin: top right;
right: .25rem;
margin-top: -.25rem;
}
.img-profile {
width: 4rem;
}
}
}
}

View File

@ -0,0 +1,459 @@
// Sidebar
.sidebar {
width: $sidebar-collapsed-width;
min-height: 100%;
ul li::marker{
display: none;
content: '';
color: transparent;
}
.nav-item {
position: relative;
&:last-child {
margin-bottom: 1rem;
}
.nav-link {
text-align: center;
padding-left: 0.75rem;
width: $sidebar-collapsed-width;
span {
font-size: 0.65rem;
display: block;
}
}
&.active {
&>.nav-link {
font-weight: 700;
}
}
// Accordion
.collapse {
position: absolute;
left: calc(#{$sidebar-collapsed-width} + #{$grid-gutter-width} / 2);
z-index: 1;
top: 2px;
// Grow In Animation
@extend .animated--grow-in;
.collapse-inner {
border-radius: $border-radius;
box-shadow: $box-shadow;
}
}
.collapsing {
display: none;
transition: none;
}
.collapse,
.collapsing {
.collapse-inner {
padding: .5rem 0;
min-width: 10rem;
font-size: $dropdown-font-size;
margin: 0 0 1rem 0;
.collapse-header {
margin: 0;
white-space: nowrap;
padding: .5rem 1.5rem;
text-transform: uppercase;
font-weight: 800;
font-size: 0.65rem;
color: $gray-500;
}
.collapse-item {
padding: 0.5rem 1rem;
margin: 0 0.5rem;
display: block;
color: $gray-900;
text-decoration: none;
border-radius: $border-radius;
white-space: nowrap;
&:hover {
background-color: $gray-200;
}
&:active {
background-color: $gray-300;
}
&.active {
color: $primary;
font-weight: 700;
}
}
}
}
}
#sidebarToggle {
width: 2.5rem;
height: 2.5rem;
text-align: center;
margin-bottom: 1rem;
cursor: pointer;
&::after {
font-weight: 900;
content: '\f104';
font-family: 'Font Awesome 5 Free';
margin-right: 0.1rem;
}
&:hover {
text-decoration: none;
}
&:focus {
outline: none;
}
}
&.toggled {
width: 0 !important;
overflow: hidden;
#sidebarToggle::after {
content: '\f105';
font-family: 'Font Awesome 5 Free';
margin-left: 0.25rem;
}
}
.sidebar-brand {
height: $topbar-base-height;
text-decoration: none;
font-size: 1rem;
font-weight: 800;
padding: 1.5rem 1rem;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.05rem;
z-index: 1;
background-color: $secondary;
.sidebar-brand-icon i {
font-size: 2rem;
}
.sidebar-brand-text {
display: none;
}
}
hr.sidebar-divider {
margin: 0 1rem 1rem;
}
.sidebar-heading {
text-align: center;
padding: 0 1rem;
font-weight: 800;
font-size: 0.65rem;
@extend .text-uppercase;
}
}
@include media-breakpoint-up(md) {
.sidebar {
width: $sidebar-base-width !important;
.nav-item {
// Accordion
.collapse {
position: relative;
left: 0;
z-index: 1;
top: 0;
animation: none;
.collapse-inner {
border-radius: 0;
box-shadow: none;
}
.sidebar-divider {
display: block;
margin: 0.5rem 1rem !important;
border-top: 1px solid #1919194f !important;
}
}
.collapsing {
display: block;
transition: $transition-collapse;
}
.collapse,
.collapsing {
margin: 0 1rem;
}
.nav-link {
display: block;
width: 100%;
text-align: left;
//padding: 1rem;
width: $sidebar-base-width;
i {
font-size: 0.85rem;
margin-right: 0.25rem;
}
span {
font-size: 0.85rem;
display: inline;
}
// Accordion Arrow Icon
&[data-toggle="collapse"] {
&::after {
width: 1rem;
text-align: center;
float: right;
vertical-align: 0;
border: 0;
font-weight: 900;
content: '\f107';
font-family: 'Font Awesome 5 Free';
}
&.collapsed::after {
content: '\f105';
}
}
}
}
.sidebar-brand {
.sidebar-brand-icon i {
font-size: 2rem;
}
.sidebar-brand-text {
display: inline;
}
}
.sidebar-heading {
text-align: left;
}
&.toggled {
overflow: visible;
width: $sidebar-collapsed-width !important;
.nav-item {
// Accordion
.collapse {
position: absolute;
left: calc(#{$sidebar-collapsed-width} + #{$grid-gutter-width} / 2);
z-index: 1;
top: 2px;
// Grow In Animation for Toggled State
animation-name: growIn;
animation-duration: 200ms;
animation-timing-function: transform cubic-bezier(.18, 1.25, .4, 1), opacity cubic-bezier(0, 1, .4, 1);
.collapse-inner {
box-shadow: $box-shadow;
border-radius: $border-radius;
}
}
.collapsing {
display: none;
transition: none;
}
.collapse,
.collapsing {
margin: 0;
}
&:last-child {
margin-bottom: 1rem;
}
.nav-link {
text-align: center;
padding: 0.75rem 1rem;
width: $sidebar-collapsed-width;
span {
font-size: 0.65rem;
display: block;
}
i {
margin-right: 0;
}
&[data-toggle="collapse"]::after {
display: none;
}
}
}
.sidebar-brand {
.sidebar-brand-icon i {
font-size: 2rem;
}
.sidebar-brand-text {
display: none;
}
}
.sidebar-heading {
text-align: center;
}
}
}
}
// Sidebar Color Variants
// Sidebar Light
.sidebar-light {
.sidebar-brand {
color: $gray-700;
}
hr.sidebar-divider {
border-top: 1px solid $gray-200;
}
.sidebar-heading {
color: $gray-500;
}
.nav-item {
.nav-link {
color: black;
i {
color: $gray-400;
}
&:active,
&:focus,
&:hover {
color: $gray-700;
i {
color: black;
}
}
// Accordion
&[data-toggle="collapse"]::after {
color: $gray-500;
}
}
&.active {
&>.nav-link {
color: $primary;
i {
color: $primary;
}
}
}
}
// Color the sidebar toggler
#sidebarToggle {
background-color: $gray-200;
&::after {
color: $gray-500;
}
&:hover {
background-color: $gray-300;
}
}
}
// Sidebar Dark
.sidebar-dark {
.sidebar-brand {
color: $white;
}
hr.sidebar-divider {
border-top: 1px solid fade-out($white, 0.85);
}
.sidebar-heading {
color: fade-out($white, 0.6);
}
.nav-item {
.nav-link {
color: fade-out($white, 0.2);
i {
color: fade-out($white, 0.7);
}
&:active,
&:focus,
&:hover {
color: $white;
i {
color: $white;
}
}
// Accordion
&[data-toggle="collapse"]::after {
color: fade-out($white, 0.5);
}
}
&>.active {
.nav-link {
color: $white;
i {
color: $white;
}
}
}
}
// Color the sidebar toggler
#sidebarToggle {
background-color: fade-out($white, 0.8);
&::after {
color: fade-out($white, 0.5);
}
&:hover {
background-color: fade-out($white, 0.75);
}
}
&.toggled {
#sidebarToggle::after {
color: fade-out($white, 0.5);
}
}
}

View File

@ -0,0 +1,130 @@
@use "sass:math";
// Topbar
.topbar {
height: $topbar-base-height;
#sidebarToggleTop {
height: 2.5rem;
width: 2.5rem;
&:hover {
background-color: $gray-200;
}
&:active {
background-color: $gray-300;
}
}
.navbar-search {
width: 25rem;
input {
font-size: 0.85rem;
}
}
.topbar-divider {
width: 0;
border-right: 1px solid $border-color;
height: calc(#{$topbar-base-height} - 2rem);
margin: auto 1rem;
}
.nav-item {
.nav-link {
height: $topbar-base-height;
display: flex;
align-items: center;
padding: 0 0.75rem;
&:focus {
outline: none;
}
}
&:focus {
outline: none;
}
}
.dropdown {
position: static;
.dropdown-menu {
width: calc(100% - #{$grid-gutter-width});
right: math.div($grid-gutter-width, 2);
}
}
.dropdown-list {
padding: 0;
border: none;
overflow: hidden;
.dropdown-header {
background-color: $primary;
border: 1px solid $primary;
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: $white;
}
.dropdown-item {
white-space: normal;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
border-bottom: 1px solid $border-color;
line-height: 1.3rem;
.dropdown-list-image {
position: relative;
height: 2.5rem;
width: 2.5rem;
img {
height: 2.5rem;
width: 2.5rem;
}
.status-indicator {
background-color: $gray-200;
height: 0.75rem;
width: 0.75rem;
border-radius: 100%;
position: absolute;
bottom: 0;
right: 0;
border: .125rem solid $white;
}
}
.text-truncate {
max-width: 10rem;
}
&:active {
background-color: $gray-200;
color: $gray-900;
}
}
}
@include media-breakpoint-up(sm) {
.dropdown {
position: relative;
.dropdown-menu {
width: auto;
right: 0;
}
}
.dropdown-list {
width: 20rem !important;
.dropdown-item {
.text-truncate {
max-width: 13.375rem;
}
}
}
}
}
.topbar.navbar-dark {}
.topbar.navbar-light {
.navbar-nav {
.nav-item {
.nav-link {
color: $gray-400;
&:hover {
color: $gray-500;
}
&:active {
color: $gray-600;
}
}
}
}
}

View File

@ -0,0 +1,20 @@
// Import Custom SB Admin 2 Variables (Overrides Default Bootstrap Variables)
@import "variables.scss";
// Import Bootstrap
// @import "../vendor/bootstrap/scss/bootstrap.scss";
// Import Custom SB Admin 2 Mixins and Components
@import "mixins.scss";
@import "global.scss";
@import "utilities.scss";
// Custom Components
@import "dropdowns.scss";
@import "navs.scss";
@import "buttons.scss";
@import "cards.scss";
@import "charts.scss";
@import "login.scss";
@import "error.scss";
@import "footer.scss";

View File

@ -0,0 +1,37 @@
// Animation Utilities
// Grow In Animation
@keyframes growIn {
0% {
transform: scale(0.9);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.animated--grow-in {
animation-name: growIn;
animation-duration: 200ms;
animation-timing-function: transform cubic-bezier(.18,1.25,.4,1), opacity cubic-bezier(0,1,.4,1);
}
// Fade In Animation
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.animated--fade-in {
animation-name: fadeIn;
animation-duration: 200ms;
animation-timing-function: opacity cubic-bezier(0,1,.4,1);
}

View File

@ -0,0 +1,75 @@
// Background Gradient Utilities
.bg-gradient-front {
background-color: $primary;
background-image: linear-gradient(180deg, $primary 10%, $secondary 100%);
background-size: cover;
}
.bg-gradient-primary {
background-color: $primary;
background-image: linear-gradient(180deg, $primary 10%, darken($primary, 15%) 100%);
background-size: cover;
}
.bg-gradient-success {
background-color: $success;
background-image: linear-gradient(180deg, $success 10%, darken($success, 15%) 100%);
background-size: cover;
}
.bg-gradient-info {
background-color: $info;
background-image: linear-gradient(180deg, $info 10%, darken($info, 15%) 100%);
background-size: cover;
}
.bg-gradient-warning {
background-color: $warning;
background-image: linear-gradient(180deg, $warning 10%, darken($warning, 15%) 100%);
background-size: cover;
}
.bg-gradient-danger {
background-color: $danger;
background-image: linear-gradient(180deg, $danger 10%, darken($danger, 15%) 100%);
background-size: cover;
}
// Grayscale Background Utilities
.bg-gray-100 {
background-color: $gray-100 !important;
}
.bg-gray-200 {
background-color: $gray-200 !important;
}
.bg-gray-300 {
background-color: $gray-300 !important;
}
.bg-gray-400 {
background-color: $gray-400 !important;
}
.bg-gray-500 {
background-color: $gray-500 !important;
}
.bg-gray-600 {
background-color: $gray-600 !important;
}
.bg-gray-700 {
background-color: $gray-700 !important;
}
.bg-gray-800 {
background-color: $gray-800 !important;
}
.bg-gray-900 {
background-color: $gray-900 !important;
}

View File

@ -0,0 +1,39 @@
.border-left-primary {
border-left: .25rem solid $primary !important;
}
.border-left-success {
border-left: .25rem solid $success !important;
}
.border-left-info {
border-left: .25rem solid $info !important;
}
.border-left-warning {
border-left: .25rem solid $warning !important;
}
.border-left-danger {
border-left: .25rem solid $danger !important;
}
.border-bottom-primary {
border-bottom: .25rem solid $primary !important;
}
.border-bottom-success {
border-bottom: .25rem solid $success !important;
}
.border-bottom-info {
border-bottom: .25rem solid $info !important;
}
.border-bottom-warning {
border-bottom: .25rem solid $warning !important;
}
.border-bottom-danger {
border-bottom: .25rem solid $danger !important;
}

View File

@ -0,0 +1,4 @@
// Overflow Hidden
.o-hidden {
overflow: hidden !important;
}

View File

@ -0,0 +1,3 @@
.progress-sm {
height: .5rem;
}

View File

@ -0,0 +1,7 @@
.rotate-15 {
transform: rotate(15deg);
}
.rotate-n-15 {
transform: rotate(-15deg);
}

View File

@ -0,0 +1,54 @@
// Grayscale Text Utilities
.text-xs {
font-size: .7rem;
}
.text-lg {
font-size: 1.2rem;
}
.text-gray-100 {
color: $gray-100 !important;
}
.text-gray-200 {
color: $gray-200 !important;
}
.text-gray-300 {
color: $gray-300 !important;
}
.text-gray-400 {
color: $gray-400 !important;
}
.text-gray-500 {
color: $gray-500 !important;
}
.text-gray-600 {
color: $gray-600 !important;
}
.text-gray-700 {
color: $gray-700 !important;
}
.text-gray-800 {
color: $gray-800 !important;
}
.text-gray-900 {
color: $gray-900 !important;
}
.icon-circle {
height: 2.5rem;
width: 2.5rem;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
}

1579
assets/styles/animate.scss vendored Normal file

File diff suppressed because it is too large Load Diff

115
assets/styles/app.scss Normal file
View File

@ -0,0 +1,115 @@
$primary: #4154f1;
$secondary: #00ffc1;
$text: #212529FF;
$blue: #012970;
@import "~bootstrap/scss/bootstrap";
@import "~tom-select/src/scss/tom-select.scss";
@import "~toastify-js/src/toastify.css";
@import "bootstrap-icons/font/bootstrap-icons.css";
@import "style";
.grow-x-1 {
transition: .3s ease-out;
&:hover {
transform: scaleX(1.01);
}
}
.grow-x-2 {
transition: .3s ease-out;
&:hover {
transform: scaleX(1.02);
}
}
.img-container {
img {
height: auto;
max-width: 100%;
}
}
.delete-button {
color: $danger;
&.selected {
background: $primary;
border-radius: 15px;
padding: 2px;
color: $white;
}
}
input.transparent {
background-color: rgba(0, 0, 0, 0);
border: none;
outline: none;
}
.cursor-pointer {
cursor: pointer;
}
.resource-image {
transition: .3s ease-out;
&:hover {
cursor: pointer;
transform: scale(1.04);
}
&.selected-image {
border: 2px solid #198754FF;
}
}
.sl-slide-holder {
height: 100%;
}
.nav-link.active {
background: $primary;
color: $white;
}
.campaign-card {
padding-top: 0.5rem;
margin-bottom: 15px;
.campaign-link {
color: $text;
}
.campaign-card-body {
transition: .3s ease-out;
&:hover {
cursor: pointer;
transform: scale(1.04);
}
}
}
.card-grow {
transition: .3s ease-out;
&:hover {
transform: scale(1.04);
}
}
.text {
color: $text;
}
@media (min-width: 768px) {
.collapse.dont-collapse-sm {
display: block;
height: auto !important;
visibility: visible;
}
}

148
assets/styles/chosen.scss Normal file
View File

@ -0,0 +1,148 @@
select.form-control + .chosen-container.chosen-container-single .chosen-single {
display: block;
width: 100%;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.428571429;
color: #555;
vertical-align: middle;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
-webkit-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
background-image:none;
}
select.form-control + .chosen-container.chosen-container-single .chosen-single div {
top:4px;
color:#000;
}
select.form-control + .chosen-container .chosen-drop {
background-color: #FFF;
border: 1px solid #CCC;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
background-clip: padding-box;
margin: 2px 0 0;
}
select.form-control + .chosen-container .chosen-search input[type=text] {
display: block;
width: 100%;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.428571429;
color: #555;
vertical-align: middle;
background-color: #FFF;
border: 1px solid #CCC;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
background-image:none;
}
select.form-control + .chosen-container .chosen-results {
margin: 2px 0 0;
padding: 5px 0;
font-size: 14px;
list-style: none;
background-color: #fff;
margin-bottom: 5px;
}
select.form-control + .chosen-container .chosen-results li ,
select.form-control + .chosen-container .chosen-results li.active-result {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.428571429;
color: #333;
white-space: nowrap;
background-image:none;
}
select.form-control + .chosen-container .chosen-results li:hover,
select.form-control + .chosen-container .chosen-results li.active-result:hover,
select.form-control + .chosen-container .chosen-results li.highlighted
{
color: #FFF;
text-decoration: none;
background-color: #428BCA;
background-image:none;
}
select.form-control + .chosen-container-multi .chosen-choices {
display: block;
width: 100%;
min-height: 34px;
padding: 6px;
font-size: 14px;
line-height: 1.428571429;
color: #555;
vertical-align: middle;
background-color: #FFF;
border: 1px solid #CCC;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
background-image:none;
}
select.form-control + .chosen-container-multi .chosen-choices li.search-field input[type="text"] {
height:auto;
padding:5px 0;
}
select.form-control + .chosen-container-multi .chosen-choices li.search-choice {
background-image: none;
padding: 3px 24px 3px 5px;
margin: 0 6px 0 0;
font-size: 14px;
font-weight: normal;
line-height: 1.428571429;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid #ccc;
border-radius: 4px;
color: #333;
background-color: #FFF;
border-color: #CCC;
}
select.form-control + .chosen-container-multi .chosen-choices li.search-choice .search-choice-close {
top:8px;
right:6px;
}
select.form-control + .chosen-container-multi.chosen-container-active .chosen-choices,
select.form-control + .chosen-container.chosen-container-single.chosen-container-active .chosen-single,
select.form-control + .chosen-container .chosen-search input[type=text]:focus{
border-color: #66AFE9;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(102, 175, 233, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(102, 175, 233, 0.6);
}
select.form-control + .chosen-container-multi .chosen-results li.result-selected{
display: list-item;
color: #ccc;
cursor: default;
background-color: white;
}

1256
assets/styles/style.scss Normal file

File diff suppressed because it is too large Load Diff

564
assets/styles/wizard.scss Normal file
View File

@ -0,0 +1,564 @@
@import "animate.scss";
.paste-styled .spinner {
width: 40px;
height: 40px;
margin: 100px auto;
background-color: white;
border-radius: 100%;
-webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
animation: sk-scaleout 1.0s infinite ease-in-out;
}
@-webkit-keyframes sk-scaleout {
0% {
-webkit-transform: scale(0);
}
100% {
-webkit-transform: scale(1);
opacity: 0;
}
}
@keyframes sk-scaleout {
0% {
-webkit-transform: scale(0);
transform: scale(0);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0;
}
}
.paste-styled .control-panel {
display: flex;
flex-direction: row;
min-height: 100vh;
}
.paste-styled .control-panel .control-panel-menu {
width: 20%;
border-right-color: lightgray;
border-right-style: solid;
border-right-width: 1px;
}
.paste-styled .control-panel .control-panel-menu ul {
display: block;
width: 100%;
}
.paste-styled .control-panel .control-panel-menu ul li {
display: block;
width: 100%;
}
.paste-styled .control-panel .control-panel-menu ul li a {
display: block;
width: 100%;
height: 48px;
line-height: 48px;
box-sizing: border-box;
padding-left: 0.5rem;
color: black;
text-decoration: none;
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
font-weight: 300;
transition: .2s;
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: transparent;
}
.paste-styled .control-panel .control-panel-menu ul li a .material-icons {
vertical-align: middle;
color: black;
margin-right: 0.5rem;
}
.paste-styled .control-panel .control-panel-menu ul li a:hover {
color: #147e93;
transition: .2s;
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: lightgray;
}
.paste-styled .control-panel .control-panel-pane {
flex: 1;
}
.paste-styled .split-h {
display: flex;
flex-direction: column;
}
.paste-styled .step-by-step {
width: 100%;
//min-height: 100vh;
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.paste-styled .step-by-step .step-by-step-step {
width: 100%;
min-height: 300px;
//border-radius: 8px;
//border-color: lightgray;
//border-width: 1px;
//border-style: solid;
height: 100%;
}
.paste-styled .step-by-step .step-by-step-step[data-active='0'] {
opacity: 0;
display: none;
}
.paste-styled .step-by-step .step-by-step-step .default-content {
height: 100%;
flex: 1;
}
.paste-styled .step-by-step .step-by-step-step label, .paste-styled .step-by-step .step-by-step-step h1, .paste-styled .step-by-step .step-by-step-step h2, .paste-styled .step-by-step .step-by-step-step h3, .paste-styled .step-by-step .step-by-step-step h4, .paste-styled .step-by-step .step-by-step-step h5, .paste-styled .step-by-step .step-by-step-step h6 {
color: #147e93;
}
.paste-styled .step-by-step .step-by-step-step .step-by-step-stepper {
width: 100%;
height: 92px;
line-height: 92px;
text-align: center;
}
.paste-styled .step-by-step .step-by-step-step .step-by-step-stepper button {
margin-left: 1rem;
margin-right: 1rem;
}
.paste-styled .dashboard {
width: 100%;
display: flex;
flex-wrap: wrap;
}
.paste-styled .dashboard .dashboard-item {
width: auto;
min-height: 200px;
margin: 0.5rem;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
flex-wrap: wrap;
}
.paste-styled .dashboard .dashboard-item .material-icons {
width: 100%;
text-align: center;
font-size: 4rem;
}
.paste-styled .drop-menu {
background-color: black;
min-width: 128px;
display: block;
position: fixed;
top: 0;
left: 0;
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
z-index: 60;
}
.paste-styled .drop-menu ul {
display: block !important;
list-style: none !important;
margin: 0px !important;
padding-left: 0px !important;
}
.paste-styled .drop-menu ul li {
display: block;
padding-left: 0px !important;
}
.paste-styled .drop-menu ul li a {
display: block;
color: white;
height: 48px;
line-height: 48px;
font-weight: 200;
box-sizing: border-box;
padding-left: 0.5rem;
padding-right: 0.5rem;
text-decoration: none;
border-bottom: none !important;
font-weight: 300;
}
.paste-styled .drop-menu ul li a:hover {
cursor: pointer;
}
.paste-styled .drop-menu ul li a .material-icons {
vertical-align: middle;
margin-left: 0.5rem;
}
.paste-styled .drop-menu .drop-menu-button {
height: 42px;
width: 42px;
line-height: 42px;
text-align: center;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
bottom: -42px;
border-bottom-right-radius: 10.5px;
}
.paste-styled .drop-menu .drop-menu-button:hover {
cursor: pointer;
}
.paste-styled .drop-menu .drop-menu-button .material-icons {
color: white;
vertical-align: middle;
}
.paste-styled .drop-menu[data-active='0'] {
display: none;
}
.paste-styled .backdrop {
position: fixed;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 70;
width: 100%;
height: 100vh;
overflow-y: hidden;
}
.paste-styled .backdrop .backdrop-content {
z-index: 80;
color: white;
}
.paste-styled .backdrop .card {
background-color: white;
color: black;
}
.paste-styled .backdrop[active="0"] {
display: none;
pointer-events: none;
}
.paste-styled .button {
line-height: 32px;
vertical-align: middle;
background: none;
border: none;
outline: none;
border-color: transparent;
border-width: 1px;
border-style: solid;
border-radius: 16px;
color: black;
font-weight: 200;
text-align: center;
padding-left: 0.8rem;
padding-right: 0.8rem;
min-width: 92px;
display: inline-block;
text-decoration: none;
transition: .2s;
background-color: lightgray;
font-weight: 300;
}
.paste-styled .button:hover {
cursor: pointer;
transition: .2s;
}
.paste-styled .button.button-warning {
background-color: #ff3b30;
color: white;
}
.paste-styled .button.button-warning:hover {
border-color: black;
color: black;
background-color: transparent;
}
.paste-styled .button.button-ok {
background-color: #4cd964;
color: white;
}
.paste-styled .button.button-ok:hover {
border-color: black;
color: black;
background-color: transparent;
}
.paste-styled .button.button-primary {
background-color: #147e93;
color: white;
}
.paste-styled .button.button-primary:hover {
border-color: black;
color: black;
background-color: transparent;
}
.paste-styled .button.button-small {
min-width: auto;
width: 42px;
height: 42px;
line-height: 42px;
text-align: center;
color: black;
border-radius: 42px;
border-color: black;
border-width: 1px;
padding-left: 0px;
padding-right: 0px;
background-color: transparent;
}
.paste-styled .button.button-small:hover {
background-color: black;
color: white;
}
.paste-styled .button.button-small-primary {
border-color: #147e93;
border-color: #147e93;
color: #147e93;
}
.paste-styled .button.button-small-primary:hover {
background-color: #147e93;
color: white;
}
.paste-styled .button[disabled] {
background-color: lightgray;
}
.paste-styled .button[disabled]:hover {
background-color: lightgray;
cursor: default;
color: white;
}
.paste-styled .form {
width: 100%;
}
.paste-styled .form .button, .paste-styled .form input, .paste-styled .form a, .paste-styled .form label, .paste-styled .form select {
margin-top: 0.5rem;
}
.paste-styled .form label, .paste-styled .form input, .paste-styled .form p, .paste-styled .form a {
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
}
.paste-styled .form label {
display: block;
}
.paste-styled .form input {
font-size: 100%;
}
.paste-styled .form input[type='text'], .paste-styled .form input[type='password'], .paste-styled .form input[type='email'] {
//display: inline-block;
//width: 100%;
//height: 48px;
//line-height: 48px;
//background: none;
//outline: none;
//border: none;
//border-bottom-color: lightgray;
//border-bottom-style: solid;
//border-bottom-width: 1px;
//font-size: 100%;
//font-weight: 300;
}
.paste-styled .form select {
display: inline-block;
height: 32px;
line-height: 32px;
background: none;
outline: none;
border: none;
border-style: solid;
border-width: 1px;
border-radius: 4px;
border-color: lightgray;
}
.paste-styled .form textarea {
display: block;
width: 100%;
min-height: 360px;
}
.paste-styled .card {
display: block;
box-sizing: border-box;
padding: 0.5rem;
border-radius: 8px;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
}
.paste-styled .simple-card {
display: block;
box-sizing: border-box;
border-radius: 8px;
border-width: 1px;
border-color: lightgray;
border-style: solid;
}
.paste-styled h1, .paste-styled h2, .paste-styled h3, .paste-styled h4, .paste-styled h5, .paste-styled h6, .paste-styled p, .paste-styled a {
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
}
.paste-styled h1, .paste-styled h2, .paste-styled h3, .paste-styled h4, .paste-styled h5, .paste-styled h6 {
font-weight: 300;
}
.paste-styled p, .paste-styled a {
font-weight: 200;
}
.paste-styled .text-warning {
color: #ff3b30 !important;
}
.paste-styled .text-ok {
color: #4cd964 !important;
}
.paste-styled .text-primary {
color: #147e93 !important;
}
.paste-styled .table {
text-align: left;
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
width: 100%;
border-collapse: collapse;
}
.paste-styled .table th, .paste-styled .table td {
padding: 0.5rem;
}
.paste-styled .table td {
font-weight: 200;
}
.paste-styled .table tr {
height: 32px;
line-height: 32px;
border-bottom-style: solid;
border-bottom-color: lightgray;
border-bottom-width: 1px;
}
* {
margin: 0;
padding: 0;
}
.paste-styled .default-content {
box-sizing: border-box;
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.paste-styled .squeezed-content {
box-sizing: border-box;
padding: 0.5rem;
}
.paste-styled .material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
/* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale;
/* Support for IE. */
font-feature-settings: 'liga';
}
.paste-styled .push-down {
margin-top: 0.5rem;
}
.paste-styled .full-height {
min-height: 100vh;
}
.paste-styled .full-width {
min-width: 100%;
}
.paste-styled .maximize-height {
min-height: 100%;
}
.paste-styled .centered-content {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.paste-styled .centered-text {
text-align: center;
}
.admin-editable p {
margin: 0px !important;
}
/*# sourceMappingURL=style.css.map */

21
bin/console Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
if (!is_dir(dirname(__DIR__).'/vendor')) {
throw new LogicException('Dependencies are missing. Try running "composer install".');
}
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
}
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
return new Application($kernel);
};

23
bin/phpunit Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env php
<?php
if (!ini_get('date.timezone')) {
ini_set('date.timezone', 'UTC');
}
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) {
if (PHP_VERSION_ID >= 80000) {
require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit';
} else {
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
PHPUnit\TextUI\Command::main();
}
} else {
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
exit(1);
}
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';
}

19
compose.override.yaml Normal file
View File

@ -0,0 +1,19 @@
version: '3'
services:
###> doctrine/doctrine-bundle ###
database:
ports:
- "5432"
###< doctrine/doctrine-bundle ###
###> symfony/mailer ###
mailer:
image: axllent/mailpit
ports:
- "1025"
- "8025"
environment:
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH_ALLOW_INSECURE: 1
###< symfony/mailer ###

21
compose.yaml Normal file
View File

@ -0,0 +1,21 @@
version: '3'
services:
###> doctrine/doctrine-bundle ###
database:
image: postgres:${POSTGRES_VERSION:-16}-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-app}
# You should definitely change the password in production
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!}
POSTGRES_USER: ${POSTGRES_USER:-app}
volumes:
- database_data:/var/lib/postgresql/data:rw
# You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data!
# - ./docker/db/data:/var/lib/postgresql/data:rw
###< doctrine/doctrine-bundle ###
volumes:
###> doctrine/doctrine-bundle ###
database_data:
###< doctrine/doctrine-bundle ###

110
composer.json Normal file
View File

@ -0,0 +1,110 @@
{
"type": "project",
"license": "proprietary",
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=8.0.2",
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/dbal": "^3",
"doctrine/doctrine-bundle": "^2.12",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/orm": "^3.1",
"phpdocumentor/reflection-docblock": "^5.4",
"phpstan/phpdoc-parser": "^1.28",
"symfony/asset": "6.0.*",
"symfony/console": "6.0.*",
"symfony/doctrine-messenger": "6.0.*",
"symfony/dotenv": "6.0.*",
"symfony/expression-language": "6.0.*",
"symfony/flex": "^2",
"symfony/form": "6.0.*",
"symfony/framework-bundle": "6.0.*",
"symfony/http-client": "6.0.*",
"symfony/intl": "6.0.*",
"symfony/mailer": "6.0.*",
"symfony/mime": "6.0.*",
"symfony/monolog-bundle": "^3.0",
"symfony/notifier": "6.0.*",
"symfony/process": "6.0.*",
"symfony/property-access": "6.0.*",
"symfony/property-info": "6.0.*",
"symfony/runtime": "6.0.*",
"symfony/security-bundle": "6.0.*",
"symfony/serializer": "6.0.*",
"symfony/stimulus-bundle": "^2.16",
"symfony/string": "6.0.*",
"symfony/translation": "6.0.*",
"symfony/twig-bundle": "6.0.*",
"symfony/ux-chartjs": "^2.16",
"symfony/ux-react": "^2.16",
"symfony/validator": "6.0.*",
"symfony/web-link": "6.0.*",
"symfony/webpack-encore-bundle": "^1.17",
"symfony/yaml": "6.0.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
},
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true,
"symfony/flex": true,
"symfony/runtime": true
},
"optimize-autoloader": true,
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"replace": {
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php72": "*",
"symfony/polyfill-php73": "*",
"symfony/polyfill-php74": "*",
"symfony/polyfill-php80": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "6.0.*"
}
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "6.0.*",
"symfony/css-selector": "6.0.*",
"symfony/debug-bundle": "6.0.*",
"symfony/maker-bundle": "^1.0",
"symfony/phpunit-bridge": "^7.0",
"symfony/stopwatch": "6.0.*",
"symfony/web-profiler-bundle": "6.0.*"
}
}

9570
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
config/bundles.php Normal file
View File

@ -0,0 +1,18 @@
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
Symfony\UX\Chartjs\ChartjsBundle::class => ['all' => true],
Symfony\UX\React\ReactBundle::class => ['all' => true],
];

View File

@ -0,0 +1,19 @@
framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name
# The "app" cache stores to the filesystem by default.
# The data in this cache should persist between deploys.
# Other options include:
# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
#app: cache.adapter.apcu
# Namespaced pools use the above "app" backend by default
#pools:
#my.dedicated.cache: null

View File

@ -0,0 +1,5 @@
when@dev:
debug:
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
# See the "server:dump" command to start a new server.
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"

View File

@ -0,0 +1,44 @@
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '16'
use_savepoints: true
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
when@test:
doctrine:
dbal:
# "TEST_TOKEN" is typically set by ParaTest
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
when@prod:
doctrine:
orm:
auto_generate_proxy_classes: false
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system

View File

@ -0,0 +1,6 @@
doctrine_migrations:
migrations_paths:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: false

View File

@ -0,0 +1,24 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
http_method_override: false
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
session:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
storage_factory_id: session.storage.factory.native
#esi: true
#fragments: true
php_errors:
log: true
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file

View File

@ -0,0 +1,3 @@
framework:
mailer:
dsn: '%env(MAILER_DSN)%'

View File

@ -0,0 +1,29 @@
framework:
messenger:
failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
use_notify: true
check_delayed_interval: 60000
retry_strategy:
max_retries: 3
multiplier: 2
failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
default_bus: messenger.bus.default
buses:
messenger.bus.default: []
routing:
Symfony\Component\Mailer\Messenger\SendEmailMessage: async
Symfony\Component\Notifier\Message\ChatMessage: async
Symfony\Component\Notifier\Message\SmsMessage: async
# Route your messages to the transports
# 'App\Message\YourMessage': async

View File

@ -0,0 +1,62 @@
monolog:
channels:
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists
when@dev:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
# type: firephp
# level: info
#chromephp:
# type: chromephp
# level: info
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
when@test:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
channels: ["!event"]
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
when@prod:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
nested:
type: stream
path: php://stderr
level: debug
formatter: monolog.formatter.json
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
deprecation:
type: stream
channels: [deprecation]
path: php://stderr
formatter: monolog.formatter.json

View File

@ -0,0 +1,12 @@
framework:
notifier:
chatter_transports:
texter_transports:
channel_policy:
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo
urgent: ['email']
high: ['email']
medium: ['email']
low: ['email']
admin_recipients:
- { email: admin@example.com }

View File

@ -0,0 +1,12 @@
framework:
router:
utf8: true
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
#default_uri: http://localhost
when@prod:
framework:
router:
strict_requirements: null

View File

@ -0,0 +1,39 @@
security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
users_in_memory: { memory: null }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: users_in_memory
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
when@test:
security:
password_hashers:
# By default, password hashers are resource intensive and take time. This is
# important to generate secure password hashes. In tests however, secure hashes
# are not important, waste resources and increase test times. The following
# reduces the work factor to the lowest possible values.
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto
cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon

View File

@ -0,0 +1,7 @@
framework:
default_locale: en
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- en
providers:

View File

@ -0,0 +1,6 @@
twig:
default_path: '%kernel.project_dir%/templates'
when@test:
twig:
strict_variables: true

View File

@ -0,0 +1,13 @@
framework:
validation:
email_validation_mode: html5
# Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping:
# App\Entity\: []
when@test:
framework:
validation:
not_compromised_password: false

View File

@ -0,0 +1,15 @@
when@dev:
web_profiler:
toolbar: true
intercept_redirects: false
framework:
profiler: { only_exceptions: false }
when@test:
web_profiler:
toolbar: false
intercept_redirects: false
framework:
profiler: { collect: false }

View File

@ -0,0 +1,45 @@
webpack_encore:
# The path where Encore is building the assets - i.e. Encore.setOutputPath()
output_path: '%kernel.project_dir%/public/build'
# If multiple builds are defined (as shown below), you can disable the default build:
# output_path: false
# Set attributes that will be rendered on all script and link tags
script_attributes:
defer: true
# Uncomment (also under link_attributes) if using Turbo Drive
# https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change
# 'data-turbo-track': reload
# link_attributes:
# Uncomment if using Turbo Drive
# 'data-turbo-track': reload
# If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
# crossorigin: 'anonymous'
# Preload all rendered script and link tags automatically via the HTTP/2 Link header
# preload: true
# Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
# strict_mode: false
# If you have multiple builds:
# builds:
# frontend: '%kernel.project_dir%/public/frontend/build'
# pass the build name as the 3rd argument to the Twig functions
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
framework:
assets:
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
#when@prod:
# webpack_encore:
# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
# # Available in version 1.2
# cache: true
#when@test:
# webpack_encore:
# strict_mode: false

5
config/preload.php Normal file
View File

@ -0,0 +1,5 @@
<?php
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) {
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php';
}

7
config/routes.yaml Normal file
View File

@ -0,0 +1,7 @@
controllers:
resource: ../src/Controller/
type: annotation
kernel:
resource: ../src/Kernel.php
type: annotation

View File

@ -0,0 +1,4 @@
when@dev:
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
prefix: /_error

View File

@ -0,0 +1,8 @@
when@dev:
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
prefix: /_profiler

24
config/services.yaml Normal file
View File

@ -0,0 +1,24 @@
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

0
migrations/.gitignore vendored Normal file
View File

51
package.json Normal file
View File

@ -0,0 +1,51 @@
{
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-react": "^7.22.15",
"@hotwired/stimulus": "^3.0.0",
"@symfony/stimulus-bridge": "^3.2.0",
"@symfony/stimulus-bundle": "file:vendor/symfony/stimulus-bundle/assets",
"@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/assets",
"@symfony/ux-react": "file:vendor/symfony/ux-react/assets",
"@symfony/webpack-encore": "^1.8.2",
"chart.js": "^3.4.1",
"core-js": "^3.0.0",
"react": "^18.0",
"react-dom": "^18.0",
"regenerator-runtime": "^0.13.2",
"sass": "^1.51.0",
"sass-loader": "^12.0.0",
"webpack-notifier": "^1.6.0"
},
"license": "UNLICENSED",
"private": true,
"scripts": {
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production --progress"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.2",
"@popperjs/core": "^2.11.5",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.1",
"chosen-js": "^1.8.7",
"codemirror": "^5.63.3",
"datatables": "^1.10.18",
"date-fns": "^2.30.0",
"htmx.org": "^1.7.0",
"jquery": "^3.6.0",
"jquery-validation": "^1.19.3",
"pdfmake": "^0.2.2",
"react-chartjs-2": "^5.2.0",
"react-select": "^5.7.7",
"react-toastify": "^9.1.3",
"summernote": "^0.8.20",
"sweetalert2": "^11.4.33",
"toastify-js": "^1.12.0",
"tom-select": "^2.2.1",
"uuid": "^9.0.1",
"webpack-jquery-ui": "^2.0.1"
}
}

38
phpunit.xml.dist Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
convertDeprecationsToExceptions="false"
>
<php>
<ini name="display_errors" value="1" />
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />
<server name="SHELL_VERBOSITY" value="-1" />
<server name="SYMFONY_PHPUNIT_REMOVE" value="" />
<server name="SYMFONY_PHPUNIT_VERSION" value="9.5" />
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners>
<extensions>
</extensions>
</phpunit>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,49 @@
/*!
* Bootstrap v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
/*!
* @kurkle/color v0.2.1
* https://github.com/kurkle/color#readme
* (c) 2022 Jukka Kurkela
* Released under the MIT License
*/
/*!
* Chart.js v3.9.1
* https://www.chartjs.org
* (c) 2022 Chart.js Contributors
* Released under the MIT License
*/
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

View File

@ -0,0 +1 @@
(self.webpackChunk=self.webpackChunk||[]).push([[524],{5926:(t,e,r)=>{var n={"./hello_controller.js":6824};function o(t){var e=u(t);return r(e)}function u(t){if(!r.o(n,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}return n[t]}o.keys=function(){return Object.keys(n)},o.resolve=u,t.exports=o,o.id=5926},2946:t=>{function e(t){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}e.keys=()=>[],e.resolve=e,e.id=2946,t.exports=e},5828:(t,e,r)=>{"use strict";r.d(e,{A:()=>u});var n=r(5978),o=r(666);const u={"symfony--ux-chartjs--chart":n.A,"symfony--ux-react--react":o.A}},6824:(t,e,r)=>{"use strict";r.r(e),r.d(e,{default:()=>a});r(2675),r(9463),r(2259),r(5700),r(3792),r(9572),r(4170),r(2892),r(9904),r(4185),r(875),r(287),r(6099),r(825),r(7764),r(2953);function n(t){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},n(t)}function o(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,u(n.key),n)}}function u(t){var e=function(t,e){if("object"!=n(t)||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var o=r.call(t,e||"default");if("object"!=n(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==n(e)?e:e+""}function i(t,e,r){return e=f(e),function(t,e){if(e&&("object"===n(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(t,c()?Reflect.construct(e,r||[],f(t).constructor):e.apply(t,r))}function c(){try{var t=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(t){}return(c=function(){return!!t})()}function f(t){return f=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},f(t)}function l(t,e){return l=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},l(t,e)}var a=function(t){function e(){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),i(this,e,arguments)}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&l(t,e)}(e,t),r=e,(n=[{key:"connect",value:function(){this.element.textContent="Hello Stimulus! Edit me in assets/controllers/hello_controller.js"}}])&&o(r.prototype,n),u&&o(r,u),Object.defineProperty(r,"prototype",{writable:!1}),r;var r,n,u}(r(2891).xI)},2882:(t,e,r)=>{"use strict";var n=r(4517);r(9336),(0,r(3066).E)(r(5926));(0,n.$)(r(2946))}},t=>{t.O(0,[710],(()=>{return e=2882,t(t.s=e);var e}));t.O()}]);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
{
"entrypoints": {
"app": {
"js": [
"/build/runtime.8ab7f0c8.js",
"/build/710.fddd3797.js",
"/build/app.2a46ef8a.js"
],
"css": [
"/build/710.47c7f965.css",
"/build/app.76b5401d.css"
]
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
{
"build/app.css": "/build/app.76b5401d.css",
"build/app.js": "/build/app.2a46ef8a.js",
"build/runtime.js": "/build/runtime.8ab7f0c8.js",
"build/710.47c7f965.css": "/build/710.47c7f965.css",
"build/710.fddd3797.js": "/build/710.fddd3797.js",
"build/fonts/bootstrap-icons.woff?": "/build/fonts/bootstrap-icons.39795c0b.woff",
"build/fonts/bootstrap-icons.woff2?": "/build/fonts/bootstrap-icons.b7bcc075.woff2"
}

View File

@ -0,0 +1 @@
(()=>{"use strict";var e,r={},t={};function o(e){var n=t[e];if(void 0!==n)return n.exports;var i=t[e]={exports:{}};return r[e].call(i.exports,i,i.exports,o),i.exports}o.m=r,e=[],o.O=(r,t,n,i)=>{if(!t){var l=1/0;for(s=0;s<e.length;s++){for(var[t,n,i]=e[s],a=!0,u=0;u<t.length;u++)(!1&i||l>=i)&&Object.keys(o.O).every((e=>o.O[e](t[u])))?t.splice(u--,1):(a=!1,i<l&&(l=i));if(a){e.splice(s--,1);var f=n();void 0!==f&&(r=f)}}return r}i=i||0;for(var s=e.length;s>0&&e[s-1][2]>i;s--)e[s]=e[s-1];e[s]=[t,n,i]},o.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return o.d(r,{a:r}),r},o.d=(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e={121:0};o.O.j=r=>0===e[r];var r=(r,t)=>{var n,i,[l,a,u]=t,f=0;if(l.some((r=>0!==e[r]))){for(n in a)o.o(a,n)&&(o.m[n]=a[n]);if(u)var s=u(o)}for(r&&r(t);f<l.length;f++)i=l[f],o.o(e,i)&&e[i]&&e[i][0](),e[i]=0;return o.O(s)},t=self.webpackChunk=self.webpackChunk||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})()})();

9
public/index.php Normal file
View File

@ -0,0 +1,9 @@
<?php
use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

0
src/Controller/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,349 @@
<?php
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use LogicException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;
abstract class BaseController extends AbstractController
{
protected const EDIT = "edit";
protected const NEW = "new";
public function isSlide(): string
{
return $this->getRequest()->isXmlHttpRequest() ? "_slide.html.twig" : ".html.twig";
}
protected function blankSlide(string $html = ""): Response
{
return $this->render("default/slide/blank.html.twig", [
'html' => $html,
]);
}
public function renderBlock(string $template, string $block, array $context = [])
{
$twig = $this->getTwig();
$template = $twig->load($template);
return new Response($template->renderBlock($block, $context));
}
protected function getTwig(): Environment
{
return $this->container->get("twig");
}
protected function widget(string $template, array $context = []): JsonResponse
{
return $this->json([
'status' => "OK",
'html' => $this->renderView($template, $context),
]);
}
protected function baseIndex(
$datatable,
$newUri = null,
$title = null,
$context = [],
$viewTemplate = 'default/index.html.twig'
): Response
{
// if ($this->isAjax()) {
// return $this->render($grid);
// }
return $this->renderAjax(
'default/slide/index.html.twig',
$viewTemplate,
array_merge([
'new_uri' => $newUri,
'datatable' => $datatable,
'title' => $title,
], $context)
);
}
/**
* @throws NotFoundExceptionInterface
* @throws ContainerExceptionInterface
*/
protected function baseDelete(
mixed $entity,
string $message,
EntityManagerInterface $entityManager = null,
callable $preDelete = null
): Response
{
$em = $entityManager ?? $this->getEm();
$request = $this->getRequest();
if ($request->isMethod(Request::METHOD_POST)) {
if (null !== $preDelete) {
$preDelete();
}
$em->remove($entity);
$em->flush();
return $this->renderAjax(
'default/slide/success.html.twig',
'default/delete.html.twig',
[
'message' => 'successfully deleted',
]
);
} else {
return $this->renderAjax(
'default/slide/delete.html.twig',
'default/delete.html.twig',
[
'message' => $message,
]
);
}
}
public function form(FormInterface $form, string|array $title, string|null $template = "default/slide/form.html.twig"): Response
{
if (is_array($title)) {
$title = implode(" ", $title);
}
$context = [
'form' => $form->createView(),
"title" => $title
];
return $this->render($template, $context);
}
protected function successSlideOrRedirect(
$redirectRoute,
$routeParams = [],
$message = null
): RedirectResponse|Response
{
if ($this->isAjax()) {
return $this->successSlide($message);
} else {
return $this->redirectToRoute($redirectRoute, $routeParams);
}
}
protected function successSlide($message = null, WidgetReloader|null $widgetReloader = null): Response
{
if ($widgetReloader) {
foreach ($widgetReloader->getWidgets() as $widget) {
$this->renderWidget($widget);
}
}
return $this->render('default/slide/success.html.twig', [
'message' => $message,
'widgetReloader' => $widgetReloader
]);
}
protected function closeSlide(WidgetReloader|null $widgetReloader = null): Response
{
if (!$widgetReloader) {
$widgetReloader = new WidgetReloader();
}
foreach ($widgetReloader->getWidgets() as $widget) {
$this->renderWidget($widget);
}
$context = ["widgetReloader" => $widgetReloader];
return $this->render("default/slide/close.html.twig", $context);
}
protected function errorSlide($message = null): Response
{
return $this->render('default/slide/error.html.twig', ['message' => $message]);
}
protected function renderAjax($ajaxTemplate, $pageTemplate, $context = []): Response
{
$request = $this->getRequest();
if ($request->isXmlHttpRequest()) {
return $this->render($ajaxTemplate, $context);
} else {
return $this->render($pageTemplate, $context);
}
}
protected function renderEdit($form): Response
{
if ($form instanceof FormInterface) {
$form = $form->createView();
}
return $this->renderAjax(
'default/slide/edit.html.twig',
'default/edit.html.twig',
[
'form' => $form,
]
);
}
protected function renderNew(
$form,
$title = null,
$ajaxTemplate = 'default/slide/new.html.twig',
$pageTemplate = 'default/new.html.twig'
): Response
{
if ($form instanceof FormInterface) {
$form = $form->createView();
}
return $this->renderAjax(
$ajaxTemplate,
$pageTemplate,
[
'form' => $form,
'title' => $title,
]
);
}
protected function renderShow($showTemplate, $context = [])
{
$parent = $this->isAjax() ? 'default/slide/show.html.twig' : 'base.html.twig';
return $this->render($showTemplate,
array_merge($context, [
'parent' => $parent,
'isSlide' => $this->isAjax()
])
);
}
protected function isAjax(): bool
{
return $this->getRequest()->isXmlHttpRequest();
}
protected function getRequest(): Request|bool|null
{
try {
return $this->container->get('request_stack')->getCurrentRequest();
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
return false;
}
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
protected function getEm(): ?EntityManagerInterface
{
return $this->container->get('em');
}
public static function getSubscribedServices(): array
{
return array_merge(parent::getSubscribedServices(), [
'em' => '?' . EntityManagerInterface::class,
]);
}
public function slide(
FormInterface $form,
string $slide,
string $class = "",
array $context = [],
string|null $template = null
): Response
{
$context = array_merge($context, [
'form' => $form->createView(),
"class" => $class,
]);
return $this->render($template ?: "default/slide/$slide.html.twig", $context);
}
protected function handleWidget(string|array $parameter): bool|JsonResponse
{
$request = $this->getRequest();
if ($widget = $request->get("widget")) {
if (is_array($parameter)) {
$context = $parameter;
$parameter = $parameter["parameter"];
} else {
$context = [$parameter => $request->attributes->get($parameter)];
}
$context = array_merge($context, $request->query->all());
return $this->widget("{$parameter}/widgets/{$widget}.html.twig", $context);
} else {
return false;
}
}
public function slideOrRender(string $regular, string $slide, array $context = []): Response
{
if ($this->getRequest()->isXmlHttpRequest()) {
return $this->render($slide, $context);
}
return $this->render($regular, $context);
}
public function disableFilter(string $filter): void
{
if (!property_exists($this, "entityManager")) {
throw new LogicException("Property \"entityManager\" does not exist in " . get_class($this));
}
$filters = $this->entityManager->getFilters();
$filters->disable($filter);
}
public function enableFilter(string $filter): void
{
if (!property_exists($this, "entityManager")) {
throw new LogicException("Property \"entityManager\" does not exist in " . get_class($this));
}
$filters = $this->entityManager->getFilters();
$filters->enable($filter);
}
protected function repo(string $class): EntityRepository
{
if (!property_exists($this, "entityManager")) {
$this->entityManager = $this->getEm();
}
return $this->entityManager->getRepository($class);
}
public function renderWidget(Widget $widget): Widget
{
$widget->setHtml($this->renderView($widget->getWidget(), $widget->getContext()));
return $widget;
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Fourbis\CmsBundle\Entity\Post\Post;
use Fourbis\CmsBundle\Entity\Post\PostTranslation;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
#[Route("/{_locale}/rss")]
class FeedController extends BaseController
{
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
#[Route("/get", name: "get_rss", options: ['expose' => true])]
public function index(RequestStack $requestStack): Response
{
$request = $requestStack->getCurrentRequest();
$baseUrl = $request->getSchemeAndHttpHost();
$locale = $request->getLocale();
$posts = $this->getAdvertisementNewsUpdates($baseUrl, $locale);
if (empty($posts)) {
return new JsonResponse(['error' => 'No posts found'], Response::HTTP_NOT_FOUND);
}
$rssFeed = $this->generateRssFeed($posts, $baseUrl, $locale, $request);
$response = new Response($rssFeed->asXML());
$response->headers->set('Content-Type', 'text/xml');
return $response;
}
private function getAdvertisementNewsUpdates(string $baseUrl, string $locale): array
{
return $this->entityManager->getRepository(Post::class)->createQueryBuilder('p')
->select('p.uuid AS post_uuid', 'pt.uuid AS translation_uuid', 'pt.title', 'pt.description', 'p.publishedAt', 'pt.content', 'pt.url', "CONCAT(:baseUrl, '/', :locale, pt.fullUrl) AS absoluteUrl, pt.locale")
->leftJoin('p.translations', 'pt')
->where('pt.fullUrl LIKE :url')
->andWhere('pt.locale = :locale')
->setParameter('url', '%/blog/%')
->setParameter('baseUrl', $baseUrl)
->setParameter('locale', $locale)
->orderBy('p.publishedAt', 'DESC')
->getQuery()
->getResult();
}
private function generateRssFeed(array $posts, string $baseUrl, string $locale, Request $request): \SimpleXMLElement
{
$rssFeed = new \SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?><rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:media=\"http://search.yahoo.com/mrss/\" xmlns:atom=\"http://www.w3.org/2005/Atom\" version='2.0'></rss>");
$channel = $rssFeed->addChild('channel');
$channel->addChild('title', '4BIS Innovation Blog Articles');
$channel->addChild('link', 'https://4bis.nl/' . $locale . '/blog');
$atomLink = $channel->addChild('link', '');
$atomLink->addAttribute('href', $request->getUri());
$atomLink->addAttribute('rel', 'self');
$atomLink->addAttribute('type', 'application/rss+xml');
$channel->addChild('language', $locale);
$currentYear = date('Y');
$channel->addChild('copyright', "4bis.nl © $currentYear All rights reserved");
$channel->addChild('description', '4bis.nl, software development and hosting partner | Maastricht, NL');
$image = $channel->addChild('image');
$image->addChild('title', '4BIS Innovation Blog Articles');
$image->addChild('url', 'https://cdn.4b.is/techlabs.4bis.co/cdn/4bis-nl/4bis-nl/2022-02-25/6218c42533d9b.4BIS%20Innovations_logo.png');
$image->addChild('link', 'https://4bis.nl/' . $locale . '/');
foreach ($posts as $post) {
$item = $channel->addChild('item');
$item->addChild('title', htmlspecialchars(strip_tags($post['title'])));
$item->addChild('link', htmlspecialchars($post['absoluteUrl']));
$atomLink = $item->addChild('atom:link', '');
$atomLink->addAttribute('href', htmlspecialchars($post['absoluteUrl']));
$atomLink->addAttribute('rel', 'standout');
$item->addChild('language', $locale);
$item->addChild('translation', $post['translation_uuid']);
$guid = $item->addChild('guid', $post['post_uuid']);
$guid->addAttribute('isPermaLink', 'false');
$item->addChild('description', htmlspecialchars(strip_tags($post['description'])));
$item->addChild('content:encoded', htmlspecialchars(strip_tags($post['content'])));
$pubDateTime = new \DateTime($post['publishedAt']->format('Y-m-d H:i:s'), new \DateTimeZone('UTC'));
$pubDateFormatted = $pubDateTime->format('D, d M Y H:i:s O');
$item->addChild('pubDate', $pubDateFormatted);
}
return $rssFeed;
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Fourbis\CmsBundle\Entity\Post\Post;
use Fourbis\CmsBundle\Entity\Post\PostTranslation;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
#[Route("/{_locale}")]
class HomeController extends BaseController
{
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
#[Route("/", name: "home_index", options: ['expose' => true])]
public function index(RequestStack $requestStack): Response
{
return $this->render('home/index.html.twig', [
]);
}
private function getAdvertisementNewsUpdates(string $baseUrl, string $locale): array
{
return $this->entityManager->getRepository(Post::class)->createQueryBuilder('p')
->select('p.uuid AS post_uuid', 'pt.uuid AS translation_uuid', 'pt.title', 'pt.description', 'p.publishedAt', 'pt.content', 'pt.url', "CONCAT(:baseUrl, '/', :locale, pt.fullUrl) AS absoluteUrl, pt.locale")
->leftJoin('p.translations', 'pt')
->where('pt.fullUrl LIKE :url')
->andWhere('pt.locale = :locale')
->setParameter('url', '%/blog/%')
->setParameter('baseUrl', $baseUrl)
->setParameter('locale', $locale)
->orderBy('p.publishedAt', 'DESC')
->getQuery()
->getResult();
}
private function generateRssFeed(array $posts, string $baseUrl, string $locale, Request $request): \SimpleXMLElement
{
$rssFeed = new \SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?><rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:media=\"http://search.yahoo.com/mrss/\" xmlns:atom=\"http://www.w3.org/2005/Atom\" version='2.0'></rss>");
$channel = $rssFeed->addChild('channel');
$channel->addChild('title', '4BIS Innovation Blog Articles');
$channel->addChild('link', 'https://4bis.nl/' . $locale . '/blog');
$atomLink = $channel->addChild('link', '');
$atomLink->addAttribute('href', $request->getUri());
$atomLink->addAttribute('rel', 'self');
$atomLink->addAttribute('type', 'application/rss+xml');
$channel->addChild('language', $locale);
$currentYear = date('Y');
$channel->addChild('copyright', "4bis.nl © $currentYear All rights reserved");
$channel->addChild('description', '4bis.nl, software development and hosting partner | Maastricht, NL');
$image = $channel->addChild('image');
$image->addChild('title', '4BIS Innovation Blog Articles');
$image->addChild('url', 'https://cdn.4b.is/techlabs.4bis.co/cdn/4bis-nl/4bis-nl/2022-02-25/6218c42533d9b.4BIS%20Innovations_logo.png');
$image->addChild('link', 'https://4bis.nl/' . $locale . '/');
foreach ($posts as $post) {
$item = $channel->addChild('item');
$item->addChild('title', htmlspecialchars(strip_tags($post['title'])));
$item->addChild('link', htmlspecialchars($post['absoluteUrl']));
$atomLink = $item->addChild('atom:link', '');
$atomLink->addAttribute('href', htmlspecialchars($post['absoluteUrl']));
$atomLink->addAttribute('rel', 'standout');
$item->addChild('language', $locale);
$item->addChild('translation', $post['translation_uuid']);
$guid = $item->addChild('guid', $post['post_uuid']);
$guid->addAttribute('isPermaLink', 'false');
$item->addChild('description', htmlspecialchars(strip_tags($post['description'])));
$item->addChild('content:encoded', htmlspecialchars(strip_tags($post['content'])));
$pubDateTime = new \DateTime($post['publishedAt']->format('Y-m-d H:i:s'), new \DateTimeZone('UTC'));
$pubDateFormatted = $pubDateTime->format('D, d M Y H:i:s O');
$item->addChild('pubDate', $pubDateFormatted);
}
return $rssFeed;
}
}

0
src/Entity/.gitignore vendored Normal file
View File

11
src/Kernel.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
}

0
src/Repository/.gitignore vendored Normal file
View File

301
symfony.lock Normal file
View File

@ -0,0 +1,301 @@
{
"doctrine/doctrine-bundle": {
"version": "2.12",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.4",
"ref": "91690c0a440faba1a3676256bcca2b4aa9f55b72"
},
"files": [
"config/packages/doctrine.yaml",
"src/Entity/.gitignore",
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-migrations-bundle": {
"version": "3.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "3.1",
"ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33"
},
"files": [
"config/packages/doctrine_migrations.yaml",
"migrations/.gitignore"
]
},
"phpunit/phpunit": {
"version": "9.6",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "9.6",
"ref": "7364a21d87e658eb363c5020c072ecfdc12e2326"
},
"files": [
".env.test",
"phpunit.xml.dist",
"tests/bootstrap.php"
]
},
"symfony/console": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "1781ff40d8a17d87cf53f8d4cf0c8346ed2bb461"
},
"files": [
"bin/console"
]
},
"symfony/debug-bundle": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "5aa8aa48234c8eb6dbdd7b3cd5d791485d2cec4b"
},
"files": [
"config/packages/debug.yaml"
]
},
"symfony/flex": {
"version": "2.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "146251ae39e06a95be0fe3d13c807bcf3938b172"
},
"files": [
".env"
]
},
"symfony/framework-bundle": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.4",
"ref": "3cd216a4d007b78d8554d44a5b1c0a446dab24fb"
},
"files": [
"config/packages/cache.yaml",
"config/packages/framework.yaml",
"config/preload.php",
"config/routes/framework.yaml",
"config/services.yaml",
"public/index.php",
"src/Controller/.gitignore",
"src/Kernel.php"
]
},
"symfony/mailer": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "4.3",
"ref": "df66ee1f226c46f01e85c29c2f7acce0596ba35a"
},
"files": [
"config/packages/mailer.yaml"
]
},
"symfony/maker-bundle": {
"version": "1.50",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/messenger": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.0",
"ref": "ba1ac4e919baba5644d31b57a3284d6ba12d52ee"
},
"files": [
"config/packages/messenger.yaml"
]
},
"symfony/monolog-bundle": {
"version": "3.10",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "3.7",
"ref": "aff23899c4440dd995907613c1dd709b6f59503f"
},
"files": [
"config/packages/monolog.yaml"
]
},
"symfony/notifier": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.0",
"ref": "178877daf79d2dbd62129dd03612cb1a2cb407cc"
},
"files": [
"config/packages/notifier.yaml"
]
},
"symfony/phpunit-bridge": {
"version": "7.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.3",
"ref": "a411a0480041243d97382cac7984f7dce7813c08"
},
"files": [
".env.test",
"bin/phpunit",
"phpunit.xml.dist",
"tests/bootstrap.php"
]
},
"symfony/routing": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.0",
"ref": "eb3b377a4dc07006c4bdb2c773652cc9434f5246"
},
"files": [
"config/packages/routing.yaml",
"config/routes.yaml"
]
},
"symfony/security-bundle": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.0",
"ref": "8a5b112826f7d3d5b07027f93786ae11a1c7de48"
},
"files": [
"config/packages/security.yaml"
]
},
"symfony/stimulus-bundle": {
"version": "2.16",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.8",
"ref": "9e33a8a3794b603fb4be6c04ee5ecab901ce549e"
}
},
"symfony/translation": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "e28e27f53663cc34f0be2837aba18e3a1bef8e7b"
},
"files": [
"config/packages/translation.yaml",
"translations/.gitignore"
]
},
"symfony/twig-bundle": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.4",
"ref": "bb2178c57eee79e6be0b297aa96fc0c0def81387"
},
"files": [
"config/packages/twig.yaml",
"templates/base.html.twig"
]
},
"symfony/ux-chartjs": {
"version": "v2.16.0"
},
"symfony/ux-react": {
"version": "2.16",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.8",
"ref": "fee05066d80fef87f20023aa637f96ca8cd415c7"
}
},
"symfony/validator": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "c32cfd98f714894c4f128bb99aa2530c1227603c"
},
"files": [
"config/packages/validator.yaml"
]
},
"symfony/web-profiler-bundle": {
"version": "6.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "24bbc3d84ef2f427f82104f766014e799eefcc3e"
},
"files": [
"config/packages/web_profiler.yaml",
"config/routes/web_profiler.yaml"
]
},
"symfony/webapp-pack": {
"version": "1.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "7d5c5e282f7e2c36a2c3bbb1504f78456c352407"
},
"files": [
"config/packages/messenger.yaml"
]
},
"symfony/webpack-encore-bundle": {
"version": "1.17",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.10",
"ref": "eff2e505d4557c967b6710fe06bd947ba555cae5"
},
"files": [
"assets/app.js",
"assets/bootstrap.js",
"assets/controllers.json",
"assets/controllers/hello_controller.js",
"assets/styles/app.css",
"config/packages/webpack_encore.yaml",
"package.json",
"webpack.config.js"
]
},
"twig/extra-bundle": {
"version": "v3.8.0"
}
}

38
templates/base.html.twig Normal file
View File

@ -0,0 +1,38 @@
{% extends "layout.html.twig" %}
{% block page_title %}{% block title %}{% endblock %}{% endblock page_title %}
{% block head_description %}
{% endblock %}
{% block gtm_head_part %}
{% endblock %}
{% block gtm_body_part %}
{% endblock %}
{% block navbar %}
{% endblock %}
{% block default_head_javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock default_head_javascripts %}
{% block head_javascripts %}
{% endblock head_javascripts %}
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
{% block above_footer %}
<!-- above footer -->
<!-- end above footer -->
{% endblock %}
{% block footer_content %}
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends 'base.html.twig' %}
{% block main_content %}
<div class="card">
<div class="card-body">
Test
</div>
</div>
{% endblock main_content %}

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block page_title %}{% endblock page_title %}</title>
{% block meta %}
<link rel="shortcut icon" href="">
<link rel="icon" type="image/x-icon" href="">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
{% if block('head_description') %}
<meta name="description" content="{% block head_description %}{% endblock %}" >
{% endif %}
<meta name="keywords" content="Technology news, Tech trends, Innovation updates, Gadgets, Tech analysis, Emerging technologies, Digital advancements, Latest News"/>
{% endblock meta %}
{% block meta_robots %}
<meta name="robots" content="index,follow"/>
{% endblock meta_robots %}
{% block stylesheets %}{% endblock stylesheets %}
{% block default_head_javascripts %}{% endblock default_head_javascripts %}
{% block head_javascripts %}{% endblock head_javascripts %}
{% block gtm_head_part %}{% endblock gtm_head_part %}
</head>
{% block body %}
<body id="page-top" class="{% block body_class %}{% endblock %}">
{% block gtm_body_part %}{% endblock %}
{% block nav %}
{% block navbar %}{% endblock %}
{% endblock %}
{% block page_wrapper %}
<div id="wrapper">
{% block page_wrapper_content %}
{% block content_wrapper %}
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
{% block content_wrapper_content %}
<!-- Main Content -->
{% block content_breadcrumbs %}{% endblock %}
{% block main_content_body %}
{% block main_content_title %}{% endblock main_content_title %}
{% block main_content %}{% endblock main_content %}
{% endblock %}
<!-- End of Main Content -->
{% endblock content_wrapper_content %}
</div>
<!-- End of Content Wrapper -->
{% endblock content_wrapper %}
{% endblock page_wrapper_content %}
</div>
{% endblock page_wrapper %}
<!-- bottom footer -->
{% block above_footer %}{% endblock %}
{% block footer_wrapper %}
{% block footer_content %}{% endblock footer_content %}
{% endblock footer_wrapper %}
<!-- /bottom footer -->
{% block default_footer_javascripts %}{% endblock default_footer_javascripts %}
{% block footer_javascripts %}{% endblock footer_javascripts %}
</body>
{% endblock body %}
</html>

11
tests/bootstrap.php Normal file
View File

@ -0,0 +1,11 @@
<?php
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
require dirname(__DIR__).'/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}

0
translations/.gitignore vendored Normal file
View File

75
webpack.config.js Normal file
View File

@ -0,0 +1,75 @@
const Encore = require('@symfony/webpack-encore');
// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
/*
* ENTRY CONFIG
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/app.js')
// enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)
.enableStimulusBridge('./assets/controllers.json')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
/*
* FEATURE CONFIG
*
* Enable & configure other features below. For a full
* list of features, see:
* https://symfony.com/doc/current/frontend.html#adding-more-features
*/
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
.configureBabel((config) => {
config.plugins.push('@babel/plugin-proposal-class-properties');
})
// enables @babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
// enables Sass/SCSS support
.enableSassLoader()
// uncomment if you use TypeScript
//.enableTypeScriptLoader()
// uncomment if you use React
.enableReactPreset()
// uncomment to get integrity="..." attributes on your script & link tags
// requires WebpackEncoreBundle 1.4 or higher
//.enableIntegrityHashes(Encore.isProduction())
// uncomment if you're having problems with a jQuery plugin
//.autoProvidejQuery()
;
module.exports = Encore.getWebpackConfig();

5588
yarn.lock Normal file

File diff suppressed because it is too large Load Diff