mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-04 22:04:59 +00:00
Allow users to display news on homepage (+ configuring a dashboard homepage)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
import './index.scss';
|
@@ -0,0 +1,7 @@
|
||||
div.flex-table {
|
||||
.news-content {
|
||||
p {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
@@ -160,3 +160,11 @@ export interface LocationType {
|
||||
contactData: "optional" | "required";
|
||||
title: TranslatableString;
|
||||
}
|
||||
|
||||
export interface NewsItemType {
|
||||
id: number;
|
||||
title: string;
|
||||
content: string;
|
||||
startDate: DateTime;
|
||||
endDate: DateTime | null;
|
||||
}
|
||||
|
@@ -97,6 +97,8 @@ import MyNotifications from './MyNotifications';
|
||||
import MyWorkflows from './MyWorkflows.vue';
|
||||
import TabCounter from './TabCounter';
|
||||
import { mapState } from "vuex";
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
@@ -112,7 +114,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'MyCustoms'
|
||||
activeTab: 'MyCustoms',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -126,8 +128,11 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
selectTab(tab) {
|
||||
this.$store.dispatch('getByTab', { tab: tab });
|
||||
if (tab !== 'MyCustoms') {
|
||||
this.$store.dispatch('getByTab', { tab: tab });
|
||||
}
|
||||
this.activeTab = tab;
|
||||
console.log(this.activeTab)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{ $t('widget.news.title') }}</h1>
|
||||
<ul v-if="newsItems.length > 0" class="scrollable">
|
||||
<NewsItem v-for="item in newsItems" :item="item" :key="item.id" />
|
||||
</ul>
|
||||
<p v-if="newsItems.length === 0 " class="chill-no-data-statement">{{ $t('widget.news.none') }}</p>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { fetchResults } from '../../../lib/api/apiMethods';
|
||||
import Modal from '../../_components/Modal.vue';
|
||||
import { NewsItemType } from '../../../types';
|
||||
import NewsItem from './NewsItem.vue';
|
||||
|
||||
const newsItems = ref<NewsItemType[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
fetchResults<NewsItemType>('/api/1.0/main/news/current.json')
|
||||
.then((news): Promise<void> => {
|
||||
// console.log('news articles', response.results)
|
||||
newsItems.value = news;
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch((error: string) => {
|
||||
console.error('Error fetching news items', error);
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<li>
|
||||
<h2>{{ props.item.title }}</h2>
|
||||
<time class="createdBy" datetime="{{item.startDate.datetime}}">{{ $d(newsItemStartDate(), 'text') }}</time>
|
||||
<div class="content" v-if="shouldTruncate(item.content)">
|
||||
<div v-html="prepareContent(item.content)"></div>
|
||||
<div class="float-end">
|
||||
<button class="btn btn-sm btn-show read-more" @click="() => openModal(item)">{{ $t('widget.news.readMore') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" v-else>
|
||||
<div v-html="convertMarkdownToHtml(item.content)"></div>
|
||||
</div>
|
||||
|
||||
<modal v-if="showModal" @close="closeModal">
|
||||
<template #header>
|
||||
<p class="news-title">{{ item.title }}</p>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="news-date">
|
||||
<time class="createdBy" datetime="{{item.startDate.datetime}}">{{ $d(newsItemStartDate(), 'text') }}</time>
|
||||
</p>
|
||||
<div v-html="convertMarkdownToHtml(item.content)"></div>
|
||||
</template>
|
||||
</modal>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { DateTime, NewsItemType } from "../../../types";
|
||||
import type { PropType } from 'vue'
|
||||
import { ref } from "vue";
|
||||
import {ISOToDatetime} from '../../../chill/js/date';
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object as PropType<NewsItemType>,
|
||||
required: true
|
||||
},
|
||||
maxLength: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 350,
|
||||
},
|
||||
maxLines: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 3
|
||||
}
|
||||
})
|
||||
|
||||
const selectedArticle = ref<NewsItemType | null>(null);
|
||||
const showModal = ref(false);
|
||||
|
||||
const openModal = (item: NewsItemType) => {
|
||||
selectedArticle.value = item;
|
||||
showModal.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
selectedArticle.value = null;
|
||||
showModal.value = false;
|
||||
};
|
||||
|
||||
const shouldTruncate = (content: string): boolean => {
|
||||
const lines = content.split('\n');
|
||||
|
||||
// Check if any line exceeds the maximum length
|
||||
const tooManyLines = lines.length > props.maxLines;
|
||||
|
||||
return content.length > props.maxLength || tooManyLines;
|
||||
};
|
||||
|
||||
const truncateContent = (content: string): string => {
|
||||
let truncatedContent = content.slice(0, props.maxLength);
|
||||
let linkDepth = 0;
|
||||
let linkStartIndex = -1;
|
||||
const lines = content.split('\n');
|
||||
|
||||
// Truncate if amount of lines are too many
|
||||
if (lines.length > props.maxLines && content.length < props.maxLength) {
|
||||
const truncatedContent = lines.slice(0, props.maxLines).join('\n').trim();
|
||||
return truncatedContent + '...';
|
||||
}
|
||||
|
||||
for (let i = 0; i < truncatedContent.length; i++) {
|
||||
const char = truncatedContent[i];
|
||||
|
||||
if (char === '[') {
|
||||
linkDepth++;
|
||||
if (linkDepth === 1) {
|
||||
linkStartIndex = i;
|
||||
}
|
||||
} else if (char === ']') {
|
||||
linkDepth = Math.max(0, linkDepth - 1);
|
||||
} else if (char === '(' && linkDepth === 0) {
|
||||
truncatedContent = truncatedContent.slice(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (linkDepth > 0) {
|
||||
truncatedContent += ']';
|
||||
linkDepth--;
|
||||
}
|
||||
|
||||
// If a link was found, append the URL inside the parentheses
|
||||
if (linkStartIndex !== -1) {
|
||||
const linkEndIndex = content.indexOf(')', linkStartIndex);
|
||||
const url = content.slice(linkStartIndex + 1, linkEndIndex);
|
||||
truncatedContent = truncatedContent.slice(0, linkStartIndex) + `(${url})`;
|
||||
}
|
||||
|
||||
truncatedContent += '...';
|
||||
|
||||
return truncatedContent;
|
||||
};
|
||||
|
||||
const preprocess = (markdown: string): string => {
|
||||
return markdown;
|
||||
}
|
||||
|
||||
const postprocess = (html: string): string => {
|
||||
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
|
||||
if ('target' in node) {
|
||||
node.setAttribute('target', '_blank');
|
||||
node.setAttribute('rel', 'noopener noreferrer');
|
||||
}
|
||||
if (!node.hasAttribute('target') && (node.hasAttribute('xlink:href') || node.hasAttribute('href'))) {
|
||||
node.setAttribute('xlink:show', 'new');
|
||||
}
|
||||
})
|
||||
|
||||
return DOMPurify.sanitize(html);
|
||||
}
|
||||
|
||||
const convertMarkdownToHtml = (markdown: string): string => {
|
||||
marked.use({'hooks': {postprocess, preprocess}});
|
||||
const rawHtml = marked(markdown);
|
||||
return rawHtml;
|
||||
};
|
||||
|
||||
const prepareContent = (content: string): string => {
|
||||
const htmlContent = convertMarkdownToHtml(content);
|
||||
return truncateContent(htmlContent);
|
||||
};
|
||||
|
||||
const newsItemStartDate = (): null|Date => {
|
||||
return ISOToDatetime(props.item?.startDate.datetime);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
li {
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
padding: .8rem;
|
||||
background-color: #fbfbfb;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1rem !important;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
font-size: .9rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.news-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
</style>
|
@@ -1,76 +1,73 @@
|
||||
<template>
|
||||
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_dashboard') }}</span>
|
||||
<div v-else id="dashboards" class="row g-3" data-masonry='{"percentPosition": true }'>
|
||||
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom1">
|
||||
<ul class="list-unstyled">
|
||||
<li v-if="counter.notifications > 0">
|
||||
<i18n-t keypath="counter.unread_notifications" tag="span" :class="counterClass" :plural="counter.notifications">
|
||||
<template v-slot:n><span>{{ counter.notifications }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.accompanyingCourses > 0">
|
||||
<i18n-t keypath="counter.assignated_courses" tag="span" :class="counterClass" :plural="counter.accompanyingCourses">
|
||||
<template v-slot:n><span>{{ counter.accompanyingCourses }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.works > 0">
|
||||
<i18n-t keypath="counter.assignated_actions" tag="span" :class="counterClass" :plural="counter.works">
|
||||
<template v-slot:n><span>{{ counter.works }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.evaluations > 0">
|
||||
<i18n-t keypath="counter.assignated_evaluations" tag="span" :class="counterClass" :plural="counter.evaluations">
|
||||
<template v-slot:n><span>{{ counter.evaluations }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.tasksAlert > 0">
|
||||
<i18n-t keypath="counter.alert_tasks" tag="span" :class="counterClass" :plural="counter.tasksAlert">
|
||||
<template v-slot:n><span>{{ counter.tasksAlert }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.tasksWarning > 0">
|
||||
<i18n-t keypath="counter.warning_tasks" tag="span" :class="counterClass" :plural="counter.tasksWarning">
|
||||
<template v-slot:n><span>{{ counter.tasksWarning }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom2">
|
||||
Mon dashboard personnalisé
|
||||
</div>
|
||||
</div>
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom3">
|
||||
Mon dashboard personnalisé
|
||||
</div>
|
||||
</div>
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom4">
|
||||
Mon dashboard personnalisé
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div v-else id="dashboards" class="container g-3">
|
||||
<div class="row">
|
||||
<div class="mbloc col-xs-12 col-sm-4">
|
||||
<div class="custom1">
|
||||
<ul class="list-unstyled">
|
||||
<li v-if="counter.notifications > 0">
|
||||
<i18n-t keypath="counter.unread_notifications" tag="span" :class="counterClass" :plural="counter.notifications">
|
||||
<template v-slot:n><span>{{ counter.notifications }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.accompanyingCourses > 0">
|
||||
<i18n-t keypath="counter.assignated_courses" tag="span" :class="counterClass" :plural="counter.accompanyingCourses">
|
||||
<template v-slot:n><span>{{ counter.accompanyingCourses }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.works > 0">
|
||||
<i18n-t keypath="counter.assignated_actions" tag="span" :class="counterClass" :plural="counter.works">
|
||||
<template v-slot:n><span>{{ counter.works }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.evaluations > 0">
|
||||
<i18n-t keypath="counter.assignated_evaluations" tag="span" :class="counterClass" :plural="counter.evaluations">
|
||||
<template v-slot:n><span>{{ counter.evaluations }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.tasksAlert > 0">
|
||||
<i18n-t keypath="counter.alert_tasks" tag="span" :class="counterClass" :plural="counter.tasksAlert">
|
||||
<template v-slot:n><span>{{ counter.tasksAlert }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
<li v-if="counter.tasksWarning > 0">
|
||||
<i18n-t keypath="counter.warning_tasks" tag="span" :class="counterClass" :plural="counter.tasksWarning">
|
||||
<template v-slot:n><span>{{ counter.tasksWarning }}</span></template>
|
||||
</i18n-t>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="this.hasDashboardItems">
|
||||
<template v-for="dashboardItem in this.dashboardItems">
|
||||
<div class="mbloc col-xs-12 col-sm-8 news" v-if="dashboardItem.type === 'news'">
|
||||
<News />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import Masonry from 'masonry-layout/masonry';
|
||||
import {makeFetch} from "ChillMainAssets/lib/api/apiMethods";
|
||||
import News from './DashboardWidgets/News.vue';
|
||||
|
||||
export default {
|
||||
name: "MyCustoms",
|
||||
components: {
|
||||
News
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
counterClass: {
|
||||
counter: true //hack to pass class 'counter' in i18n-t
|
||||
}
|
||||
},
|
||||
dashboardItems: [],
|
||||
masonry: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -78,11 +75,19 @@ export default {
|
||||
noResults() {
|
||||
return false
|
||||
},
|
||||
hasDashboardItems() {
|
||||
return this.dashboardItems.length > 0;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const elem = document.querySelector('#dashboards');
|
||||
const masonry = new Masonry(elem, {});
|
||||
}
|
||||
makeFetch('GET', '/api/1.0/main/dashboard-config-item.json')
|
||||
.then((response) => {
|
||||
this.dashboardItems = response;
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -98,4 +103,10 @@ span.counter {
|
||||
background-color: unset;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
div.news {
|
||||
max-height: 22rem;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
|
@@ -63,7 +63,15 @@ const appMessages = {
|
||||
},
|
||||
emergency: "Urgent",
|
||||
confidential: "Confidentiel",
|
||||
automatic_notification: "Notification automatique"
|
||||
automatic_notification: "Notification automatique",
|
||||
widget: {
|
||||
news: {
|
||||
title: "Actualités",
|
||||
readMore: "Lire la suite",
|
||||
date: "Date",
|
||||
none: "Aucune actualité"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -96,13 +96,11 @@ const store = createStore({
|
||||
},
|
||||
catchError(state, error) {
|
||||
state.errorMsg.push(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
getByTab({ commit, getters }, { tab, param }) {
|
||||
switch (tab) {
|
||||
case 'MyCustoms':
|
||||
break;
|
||||
// case 'MyWorks':
|
||||
// if (!getters.isWorksLoaded) {
|
||||
// commit('setLoading', true);
|
||||
@@ -221,8 +219,8 @@ const store = createStore({
|
||||
default:
|
||||
throw 'tab '+ tab;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { store };
|
||||
export { store };
|
||||
|
@@ -15,18 +15,20 @@ import AddressModal from "./AddressModal.vue";
|
||||
|
||||
export interface AddressModalContentProps {
|
||||
address_id: number;
|
||||
address_ref_status: AddressRefStatus | null;
|
||||
address_ref_status: AddressRefStatus;
|
||||
}
|
||||
|
||||
const data = reactive<{
|
||||
loading: boolean,
|
||||
working_address: Address | null,
|
||||
working_ref_status: AddressRefStatus | null,
|
||||
}>({
|
||||
interface AddressModalData {
|
||||
loading: boolean,
|
||||
working_address: Address | null,
|
||||
working_ref_status: AddressRefStatus | null,
|
||||
}
|
||||
|
||||
const data: AddressModalData = reactive({
|
||||
loading: false,
|
||||
working_address: null,
|
||||
working_ref_status: null,
|
||||
});
|
||||
} as AddressModalData);
|
||||
|
||||
const props = defineProps<AddressModalContentProps>();
|
||||
|
||||
|
@@ -51,7 +51,7 @@ const messages = {
|
||||
years_old: "1 an | {n} an | {n} ans",
|
||||
residential_address: "Adresse de résidence",
|
||||
located_at: "réside chez"
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -0,0 +1,13 @@
|
||||
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
|
||||
|
||||
{% block vertical_menu_content %}
|
||||
{{ chill_menu('admin_news_item', {
|
||||
'layout': '@ChillMain/Admin/menu_admin_section.html.twig',
|
||||
}) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block layout_wvm_content %}
|
||||
{% block admin_content %}<!-- block content empty -->
|
||||
<h1>{{ 'admin.dashboard.description' | trans }}</h1>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@@ -1 +1 @@
|
||||
{{ 'crud.%crud_name%.title_view'|trans({'%crud_name%' : crud_name }) }}
|
||||
{{ ('crud.' ~ crud_name ~ '.title_view')|trans({'%crud_name%' : crud_name }) }}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<h3>
|
||||
{{ entity.title }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<p>
|
||||
{% if entity.startDate %}
|
||||
<span>{{ entity.startDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
{% if entity.endDate %}
|
||||
<span> - {{ entity.endDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div>
|
||||
{{ entity.content|u.truncate(350, '… [' ~ ('news.read_more'|trans) ~ '](' ~ chill_path_add_return_path('chill_main_single_news_item', { 'id': entity.id } ) ~ ')', false)|chill_markdown_to_html }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_main_single_news_item', { 'id': entity.id } ) }}" class="btn btn-show btn-mini"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -0,0 +1,15 @@
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc">
|
||||
<p class="date-label">
|
||||
<span>{{ entity.startDate|format_date('long') }}</span>
|
||||
{% if entity.endDate is not null %}
|
||||
<span> - {{ entity.endDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="news-content">
|
||||
{{ entity.content|chill_markdown_to_html }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,6 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_delete_content.html.twig' %}
|
||||
{% endembed %}
|
||||
{% endblock admin_content %}
|
@@ -0,0 +1,11 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_edit_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock admin_content %}
|
@@ -0,0 +1,43 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
||||
{% block table_entities_thead_tr %}
|
||||
<th>{{ 'Title'|trans }}</th>
|
||||
<th>{{ 'news.startDate'|trans }}</th>
|
||||
<th>{{ 'news.endDate'|trans }}</th>
|
||||
{% endblock %}
|
||||
{% block table_entities_tbody %}
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.title }}</td>
|
||||
<td>{{ entity.startDate|format_date('long') }}</td>
|
||||
{% if entity.endDate is not null %}
|
||||
<td>{{ entity.endDate|format_date('long') }}</td>
|
||||
{% else %}
|
||||
<td>{{ 'news.noDate'|trans }}</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_news_item_view', { 'id': entity.id }) }}" class="btn btn-show" title="{{ 'show'|trans }}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_news_item_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_news_item_delete', { 'id': entity.id }) }}" class="btn btn-delete" title="{{ 'delete'|trans }}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block actions_before %}
|
||||
<li class='cancel'>
|
||||
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
@@ -0,0 +1,11 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock admin_content %}
|
@@ -0,0 +1,70 @@
|
||||
{% extends "@ChillMain/layout.html.twig" %}
|
||||
|
||||
{% block title %}
|
||||
{{ 'news.title'|trans }}
|
||||
{% endblock title %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_news') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10 asideactivity-list">
|
||||
<h2>{{ 'news.title'|trans }}</h2>
|
||||
|
||||
{{ filter_order|chill_render_filter_order_helper }}
|
||||
|
||||
{% if entities|length == 0 %}
|
||||
<p class="chill-no-data-statement">
|
||||
{{ "news.no_data"|trans }}
|
||||
</p>
|
||||
{% else %}
|
||||
|
||||
<div class="flex-table">
|
||||
|
||||
{% for entity in entities %}
|
||||
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="wrap-list">
|
||||
<div class="wl-row">
|
||||
<div class="wl-col">
|
||||
<h2>{{ entity.title }}</h2>
|
||||
</div>
|
||||
<div class="wl-col">
|
||||
<p>
|
||||
{% if entity.startDate %}
|
||||
<span>{{ entity.startDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
{% if entity.endDate %}
|
||||
<span> - {{ entity.endDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item-row separator">
|
||||
<div>
|
||||
{{ entity.content|u.truncate(350, '…', false)|chill_markdown_to_html }}
|
||||
</div>
|
||||
</div>
|
||||
{% if entity.content|length > 350 %}
|
||||
<div class="item-row">
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_main_single_news_item', { 'id': entity.id } ) }}" class="btn btn-show btn-sm read-more">{{ 'news.read_more'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ chill_pagination(paginator) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,24 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title 'news.show_details'|trans %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_news') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10 col-xxl">
|
||||
<div class="news-item-show">
|
||||
<h1>{{ entity.title }}</h1>
|
||||
|
||||
{% include '@ChillMain/NewsItem/_show.html.twig' with { 'entity': entity } %}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ chill_return_path_or('chill_main_news_items_history') }}" class="btn btn-cancel">{{ 'Back to the list'|trans|chill_return_path_label }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,34 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_view_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_news') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
|
||||
<div class="col-md-10 col-xxl">
|
||||
<div class="news-item-show">
|
||||
<h1>{{ entity.title }}</h1>
|
||||
|
||||
{% include '@ChillMain/NewsItem/_show.html.twig' with { 'entity': entity } %}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ chill_return_path_or('chill_crud_news_item_index') }}" class="btn btn-cancel">{{ 'Back to the list'|trans|chill_return_path_label }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_news_item_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_news_item_delete', { 'id': entity.id }) }}" class="btn btn-delete" title="{{ 'delete'|trans }}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
Reference in New Issue
Block a user