diff --git a/public/css/main.css b/public/css/main.css index e68d894..816a338 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -1,12 +1,460 @@ +main { + font-family: "Segoe UI", Arial, sans-serif; +} + +/* New Header Styles */ +.lth-navbar { + background-color: white; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + padding: 0.5rem 1rem; +} + +.lth-brand { + display: flex; + align-items: center; + gap: 12px; +} + +.lth-logo { + height: 40px; + width: auto; +} + +.lth-brand-text { + display: flex; + flex-direction: column; +} + +.lth-brand-title { + font-size: 18px; + font-weight: 600; + color: #2e333d; + line-height: 1.2; +} + +.lth-brand-subtitle { + font-size: 12px; + color: #6b7280; + line-height: 1.2; +} + +.lth-nav-item { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 16px !important; + border-radius: 6px; + margin: 0 4px; + color: #2e333d !important; + transition: all 0.2s ease; +} + +.lth-nav-item:hover { + background-color: #f3f4f6; +} + +.lth-nav-item.is-active { + background-color: #eb0f0f; + color: white !important; +} + +.lth-nav-item.is-active .icon { + color: white; +} + +.lth-nav-item .icon { + color: #6b7280; +} + +.lth-nav-item:hover .icon { + color: #eb0f0f; +} + +.lth-nav-item.is-active:hover { + background-color: #d10d0d; +} + +.lth-nav-item.is-active:hover .icon { + color: white; +} + +.navbar-burger span { + background-color: #2e333d; +} + +/* Footer Styles */ +.lth-footer { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background-color: white; + padding: 16px 24px; + border-top: 1px solid #e5e7eb; + box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.05); + z-index: 30; +} + +.lth-footer-content { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 1200px; + margin: 0 auto; +} + +.lth-footer-left { + display: flex; + align-items: center; + gap: 24px; +} + +.lth-footer-brand { + color: #2e333d; + font-weight: 600; + font-size: 16px; + text-decoration: none; +} + +.lth-footer-brand:hover { + color: #eb0f0f; +} + +.lth-footer-socials { + display: flex; + gap: 16px; +} + +.lth-footer-socials a { + color: #6b7280; + font-size: 18px; + transition: color 0.2s ease; +} + +.lth-footer-socials a:hover { + color: #eb0f0f; +} + +.lth-footer-link { + color: #6b7280; + text-decoration: none; + font-size: 14px; + transition: color 0.2s ease; +} + +.lth-footer-link:hover { + color: #eb0f0f; +} + +@media (max-width: 600px) { + .lth-footer-content { + flex-direction: column; + gap: 16px; + text-align: center; + } + + .lth-footer-left { + flex-direction: column; + gap: 12px; + } +} + .container { - padding: 20px; + padding: 20px; } .cell { - display: grid; - height: 100%; + display: grid; + height: 100%; +} + +.title { + font-weight: 650; + --bulma-block-spacing: 2rem; +} + +.icon { + color: #eb0f0f; +} + +.grid { + gap: 2rem; } #calendar { - height: 800px; + height: 800px; +} + +.community-image { + justify-content: center; + display: flex; + flex-direction: column; + align-items: center; +} + +.toastui-calendar-detail-item-indent { + display: none !important; +} + +.calendar-header { + display: flex; + flex-direction: row; + gap: 10px; + align-items: center; + font-size: 20px; +} + +#calendarMobileList, +.calendar-mobile-header { + display: none; +} + +@media (max-width: 768px) { + #calendar, + .calendar-header:not(.calendar-mobile-header) { + display: none; + } + #calendarMobileList { + display: block; + } + .calendar-mobile-header { + display: flex; + font-size: 16px; + } + #calendarMobileList .lth-card { + padding: 16px 18px; + } +} + +.calendar-popup-text { + color: #2e333d; +} + +.calendar-popup-link-wrap { + margin-top: 8px; +} + +.calendar-popup-link { + color: #c00d0d; + font-weight: 600; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 6px; +} + +.calendar-popup-link:hover { + text-decoration: underline; +} + +.lth-card { + display: flex; + flex-direction: column; + gap: 18px; + padding: 24px 30px; + border-radius: 6px; + border: 1px solid #d3d3d3; + color: #2e333d; + white-space: wrap; +} + +.lth-card-icon { + display: flex; + font-size: 48px; + margin-bottom: 24px; + color: #eb0f0f; +} + +.lth-card-title { + font-size: 24px; + font-weight: 600; + line-height: 28px; +} + +.lth-card-text { + font-size: 18px; + line-height: 26px; +} + +.lth-card-list { + padding-left: 20px; + margin: 0; +} + +.lth-card-list li { + margin-bottom: 8px; +} + +.lth-card-list li:last-child { + margin-bottom: 0; +} + +.lth-card h4 { + font-size: 18px; + font-weight: 500; + margin-top: 8px; + color: #2e333d; +} + +.lth-card h4 i { + color: #eb0f0f; + margin-right: 6px; +} + +.lth-card-reference { + max-width: 300px; + justify-content: space-between; +} + +.learn-more:after { + content: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='11' fill='none' viewBox='0 0 7 11'%3E%3Cpath stroke='%23eb0f0f' stroke-width='1.2' d='M1 1.301 5.199 5.5 1 9.699'/%3E%3C/svg%3E"); + margin-left: 8px; + position: relative; + vertical-align: 1px; +} + +.learn-more { + width: fit-content; + background-image: linear-gradient(#000, #000); + background-position-x: 0; + background-position-y: 100%; + background-repeat: no-repeat; + background-size: 0 .1em; + color: #000 !important; + transition: background-size .2s ease-in-out; +} + +.learn-more:hover { + background-size: 100% .1em; +} + + +.search-header { + max-width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: 32px; +} + +@media (max-width: 600px) { + .search-header { + flex-direction: column; + align-items: flex-start; + gap: 16px; + } +} + +.search-box { + position: relative; + display: flex; + align-items: center; +} + +.search-icon { + position: absolute; + left: 16px; + color: #9ca3af; + font-size: 14px; + pointer-events: none; +} + +.search-input { + padding: 12px 16px 12px 44px; + border-radius: 50px; + border: 1px solid #e5e7eb; + width: 400px; + font-size: 16px; + background-color: #fff; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.search-input:focus { + outline: none; + border-color: #eb0f0f; + box-shadow: 0 0 0 3px rgba(235, 15, 15, 0.1); +} + +.search-input::placeholder { + color: #9ca3af; +} + +@media (max-width: 600px) { + .search-input { + width: 100%; + } +} + +.search-header-link { + color: #6b7280; + text-decoration: none; + font-size: 14px; + transition: color 0.2s ease; +} + +.search-header-link:hover { + color: #eb0f0f; +} + +/* About Page */ + +.about-contact { + background-color: #eb0f0f; + padding: 40px 24px; + text-align: center; + border-radius: 16px; + max-width: 900px; + margin: 0 auto 80px; +} + +.about-contact-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; +} + +.about-contact-icon { + display: flex; + align-items: center; + justify-content: center; + width: 64px; + height: 64px; + border-radius: 50%; + background-color: rgba(255, 255, 255, 0.2); + font-size: 28px; + color: white; + margin-bottom: 8px; +} + +.about-contact-title { + font-size: 32px; + font-weight: 600; + color: white; + margin: 0; +} + +.about-contact-subtitle { + font-size: 18px; + color: rgba(255, 255, 255, 0.9); + margin: 0; +} + +.about-contact-links { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 16px; +} + +.about-contact-links a { + color: white; + text-decoration: underline; + font-size: 16px; +} + + +.about-contact-links i { + margin-right: 8px; } diff --git a/public/js/communityEvents.html b/public/js/communityEvents.html index 714169e..dbd4dbb 100644 --- a/public/js/communityEvents.html +++ b/public/js/communityEvents.html @@ -1,8 +1,7 @@ -
+
-

{{ paneTitle }}

{{#if noEvent}} -

+

{{ noEventCaption }}

{{/if}} @@ -16,12 +15,12 @@

{{#if hasUrl}} - {{ title }} + {{ title }} {{else}} {{ title }} {{/if}}

-

De {{ startDateHour }} à {{ endDateHour }} - {{ location }}

+

De {{ startDateHour }} à {{ endDateHour }}{{#if location}} - {{ location }}{{/if}}

{{#if hasDescription}}

{{ description }}

{{/if}} diff --git a/public/js/main.js b/public/js/main.js index c9e9b0d..227e3b3 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -23,14 +23,6 @@ const toDescription = component => { return description; }; -const meetupUrlFor = description => { - const matching = description.match(/(https:\/\/www.meetup.com\/[a-zA-Z0-9-]+\/events\/[0-9]+)/g); - if (matching && matching.length > 0) { - return matching[matching.length - 1]; - } - return undefined; -}; - const toEvent = (component, index) => { const description = toDescription(component); const startDate = component.getFirstPropertyValue('dtstart').toJSDate(); @@ -38,7 +30,7 @@ const toEvent = (component, index) => { const format = (d) => d.toString().padStart(2, '0'); const formatHour = (d) => format(d.getHours()) + 'H' + format(d.getMinutes()); const months = ['Jan', 'Fev', 'Mars', 'Avr', 'Mai', 'Juin', 'Juil', 'Aout', 'Sept', 'Oct', 'Nov', 'Dec']; - const url = meetupUrlFor(description); + const url = component.getFirstPropertyValue('url'); return { id: component.getFirstPropertyValue('uid'), title: component.getFirstPropertyValue('summary'), @@ -61,15 +53,35 @@ const matchPatternForEvent = event => pattern => event.title.toLowerCase().inclu const matchForPatterns = patterns => event => patterns.some(matchPatternForEvent(event)); -const filterForPeriod = (minDate, maxDate) => event => event.startDate >= minDate && event.endDate <= maxDate; +const filterForPeriod = (minDate, maxDate) => event => event.startDate < maxDate && event.endDate > minDate; const listVEventComponents = raw => new ICAL.Component(ICAL.parse(raw)).getAllSubcomponents('vevent'); +const escapeHtml = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => + ({ '&':'&', '<':'<', '>':'>', '"':'"', "'":''' }[c])); + +const safeUrl = (url) => { + if (!url) return ''; + let parsed; + try { parsed = new URL(url, document.baseURI); } catch { return ''; } + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return ''; + return parsed.href; +}; + const calendarICSUrl = 'https://www.lyontechhub.org/Lyon-Tech-Hub-Calendar/calendar.ics'; -const fetchEvents = (patterns, minDate, maxDate) => fetch(calendarICSUrl).then((response) => response.text()).then((raw) => - listVEventComponents(raw) - .map(toEvent) +let _icsEventsP; +const fetchAllRawEvents = () => { + if (!_icsEventsP) { + _icsEventsP = fetch(calendarICSUrl) + .then((response) => response.text()) + .then((raw) => listVEventComponents(raw).map(toEvent)); + } + return _icsEventsP; +}; + +const fetchEvents = (patterns, minDate, maxDate) => fetchAllRawEvents().then((events) => + events .filter(filterForPeriod(minDate, maxDate)) .filter(matchForPatterns(patterns))); @@ -90,6 +102,12 @@ const loadCommunities = () => .then((response) => response.text()) .then((body) => JSON.parse(body)); +const refreshCurrentMonth = (calendar) =>{ + let dateRangeStart = calendar.getDate(); + const monthNames = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre']; + document.querySelector('#calendarDate').textContent = monthNames[dateRangeStart.getMonth()] + ' ' + dateRangeStart.getFullYear(); +} + const loadCalendar = async () => { const communities = await loadCommunities(); const communitiesCalendars = @@ -103,6 +121,18 @@ const loadCalendar = async () => { }); const Calendar = tui.Calendar; + const capitalize = (s) => s ? s.charAt(0).toUpperCase() + s.slice(1) : s; + const dateFmt = new Intl.DateTimeFormat('fr-FR', { + weekday: 'long', day: 'numeric', month: 'long', year: 'numeric', + timeZone: 'Europe/Paris', + }); + const timeFmt = new Intl.DateTimeFormat('fr-FR', { + hour: '2-digit', minute: '2-digit', hour12: false, + timeZone: 'Europe/Paris', + }); + const formatFrTime = (d) => timeFmt.format(d).replace(':', 'h'); + const toJsDate = (d) => (d && typeof d.toDate === 'function') ? d.toDate() : new Date(d); + const calendar = new Calendar('#calendar', { usageStatistics: false, defaultView: 'month', @@ -119,6 +149,20 @@ const loadCalendar = async () => { }, ], }, + template: { + popupDetailDate({ start, end, isAllday }) { + const startDate = toJsDate(start); + const endDate = toJsDate(end); + const startStr = capitalize(dateFmt.format(startDate)); + if (isAllday) return startStr; + const sameDay = dateFmt.format(startDate) === dateFmt.format(endDate); + if (sameDay) { + return `${startStr}, ${formatFrTime(startDate)} - ${formatFrTime(endDate)}`; + } + const endStr = capitalize(dateFmt.format(endDate)); + return `${startStr}, ${formatFrTime(startDate)} → ${endStr}, ${formatFrTime(endDate)}`; + }, + }, calendars: [ { id: 'default', @@ -129,9 +173,49 @@ const loadCalendar = async () => { ], }); - fetch(calendarICSUrl) - .then((response) => response.text()) - .then((raw) => listVEventComponents(raw).map(toEvent)) + // Workaround Toast UI Calendar 2.1.3 popup offset bug: the lib writes + // document-relative top/left on a popup whose CSS containing block is + // whatever the closest positioned ancestor happens to be (here Bulma's + // .container, since the popup is portalled into a floating-layer that is + // a sibling of the calendar layout, not a descendant). We re-anchor by + // subtracting the popup's actual offsetParent's document offset. + const calendarRoot = document.querySelector('#calendar'); + if (calendarRoot) { + const fixPopupPosition = () => { + const popup = calendarRoot.querySelector('.toastui-calendar-popup-container'); + if (!popup || !popup.style.top || !popup.style.left) return; + const op = popup.offsetParent; + if (!op) return; + const r = op.getBoundingClientRect(); + const dy = r.top + window.scrollY; + const dx = r.left + window.scrollX; + const key = popup.style.top + '|' + popup.style.left; + if (popup.dataset.lthPosKey === key) return; + const t = parseFloat(popup.style.top); + const l = parseFloat(popup.style.left); + if (Number.isNaN(t) || Number.isNaN(l)) return; + popup.style.top = (t - dy) + 'px'; + popup.style.left = (l - dx) + 'px'; + popup.dataset.lthPosKey = popup.style.top + '|' + popup.style.left; + }; + const attachObserver = () => { + const layer = calendarRoot.querySelector('.toastui-calendar-floating-layer'); + if (!layer) { + setTimeout(attachObserver, 50); + return; + } + new MutationObserver(fixPopupPosition).observe(layer, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ['style'], + }); + }; + attachObserver(); + } + + refreshCurrentMonth(calendar); + fetchAllRawEvents() .then((items) => { calendar.createEvents( items.map((item) => { @@ -144,7 +228,7 @@ const loadCalendar = async () => { if (patterns) { for (var j = 0; j < patterns.length; j++) { if (match[1].localeCompare(patterns[j], 'en', { sensitivity: 'base' }) === 0) { - title = '[' + match[1] + '] ' + match[2]; + title = '[' + patterns[j] + '] ' + match[2]; calendarId = communities[i].key; break; } @@ -153,25 +237,128 @@ const loadCalendar = async () => { } } + function formatWithLink(text, url) { + const safeText = escapeHtml(text); + const href = safeUrl(url); + return href + ? `${safeText}` + : safeText; + } + + const safeItemUrl = safeUrl(item.url); + const truncated = truncate(item.description, 200); + const truncatedHtml = escapeHtml(truncated || ''); + const linkHtml = safeItemUrl + ? `` + : ''; + const body = truncatedHtml && linkHtml + ? `${truncatedHtml}${linkHtml}` + : (truncatedHtml || linkHtml); + return { calendarId: calendarId, id: item.id, - title: title, - body: item.description, + title: formatWithLink(title, safeItemUrl), + body, start: item.startDate, end: item.endDate, location: item.location, - raw: { url: item.url }, + state: '', + raw: { url: safeItemUrl }, + isReadOnly: true, } }) ); }) ; - document.querySelector('#calendarToday').onclick = () => { calendar.today(); }; - document.querySelector('#calendarNext').onclick = () => { calendar.next(); }; - document.querySelector('#calendarPrevious').onclick = () => { calendar.prev(); }; + document.querySelector('#calendarToday').addEventListener('click', () => { + calendar.today(); + refreshCurrentMonth(calendar); + }); + document.querySelector('#calendarNext').addEventListener('click', () => { + calendar.next(); + refreshCurrentMonth(calendar); + }); + document.querySelector('#calendarPrevious').addEventListener('click', () => { + calendar.prev(); + refreshCurrentMonth(calendar); + }); + +}; + +const startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()); + +const truncate = (text, max) => { + if (!text) return text; + const chars = [...text]; + if (chars.length <= max) return text; + return chars.slice(0, max).join('').replace(/[.\s…]+$/, '') + '…'; +}; + +const fetchAllEvents = (minDate, maxDate) => + fetchAllRawEvents().then((events) => events.filter(filterForPeriod(minDate, maxDate))); + +const loadCalendarMobileList = async () => { + const el = document.getElementById('calendarMobileList'); + if (!el) return; + const rangeEl = document.getElementById('calendarMobileRange'); + const prevEl = document.getElementById('calendarMobilePrevious'); + const nextEl = document.getElementById('calendarMobileNext'); + const todayEl = document.getElementById('calendarMobileToday'); + + const template = Handlebars.compile( + await fetch('/js/communityEvents.html').then((r) => r.text()) + ); + + const WINDOW_DAYS = 14; + const monthLabels = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', + 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre']; + const formatRange = (start, endExclusive) => { + const last = new Date(endExclusive); + last.setDate(last.getDate() - 1); + return `${start.getDate()} ${monthLabels[start.getMonth()]}`; + }; + + let windowStart = startOfDay(new Date()); + let renderToken = 0; + + const render = async (start) => { + const myToken = ++renderToken; + const windowEnd = new Date(start); + windowEnd.setDate(windowEnd.getDate() + WINDOW_DAYS); + if (rangeEl) rangeEl.textContent = formatRange(start, windowEnd); + const events = (await fetchAllEvents(start, windowEnd)) + .toSorted((a, b) => a.startDate - b.startDate) + .map((ev, i) => { + const url = safeUrl(ev.url); + return { + ...ev, + url, + hasUrl: Boolean(url), + description: truncate(ev.description, 200), + isNotFirst: i > 0, + }; + }); + if (myToken !== renderToken) return; + displayEvents(template, el, events); + }; + + const shiftBy = (days) => { + const next = new Date(windowStart); + next.setDate(next.getDate() + days); + windowStart = next; + render(windowStart); + }; + + prevEl?.addEventListener('click', () => shiftBy(-1)); + nextEl?.addEventListener('click', () => shiftBy(1)); + todayEl?.addEventListener('click', () => { + windowStart = startOfDay(new Date()); + render(windowStart); + }); + render(windowStart); }; window.onload = () => { @@ -195,15 +382,19 @@ window.onload = () => { fourMonthAgo, fourMonthLater ).then((items) => { + const sanitized = items.map((item) => { + const url = safeUrl(item.url); + return { ...item, url, hasUrl: Boolean(url) }; + }); displayEvents( compiledTemplate, pastEventsElement, - items.filter((item) => item.startDate < now).toSorted((a, b) => b.startDate - a.startDate) + sanitized.filter((item) => item.startDate < now).toSorted((a, b) => b.startDate - a.startDate) ); displayEvents( compiledTemplate, upcomingEventsElement, - items.filter((item) => item.startDate >= now).toSorted((a, b) => a.startDate - b.startDate) + sanitized.filter((item) => item.startDate >= now).toSorted((a, b) => a.startDate - b.startDate) ); }); }); @@ -213,4 +404,5 @@ window.onload = () => { if (calendarElement) { loadCalendar(); } + loadCalendarMobileList(); } diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 7f86430..d764b0d 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -2,7 +2,14 @@ interface Props { } -// const { title } = Astro.props; +const currentPath = Astro.url.pathname; + +function isActive(href: string): boolean { + if (href === '/') { + return currentPath === '/' || currentPath === ''; + } + return currentPath.startsWith(href); +} --- @@ -12,111 +19,106 @@ interface Props { Lyon Tech Hub - + - - + + + -
diff --git a/src/pages/about.astro b/src/pages/about.astro index 90cf901..2d3844a 100644 --- a/src/pages/about.astro +++ b/src/pages/about.astro @@ -3,90 +3,84 @@ import Layout from '../layouts/Layout.astro'; --- -
-
-
-
-
-

Qui sommes-nous ?

+
+
+
+

À propos de Lyon Tech Hub

+
+
+
+ +

Qui sommes-nous ?

+

Lyon Tech Hub est un groupe informel constitué des membres des + différentes communautés IT/tech lyonnaises. + Le groupe est auto-géré par les membres les plus actifs.

+

Les membres sont des passionnés, des experts, des curieux, des + professionnels de tous horizons: éditeurs, sociétés industrielles, startups, indépendants, sociétés de service... + Ils sont réunis par la volonté d'apprendre et d'échanger.

+
+
+
+
+ +

Rejoignez-nous !

+

Sur Slack

+

Pour discuter avec les membres de nos communautés sur Slack, + inscrivez-vous sur la page + https://slack.lyontechhub.org. +

+

Évènements

+

+ N'hésitez pas à participer aux événements organisés par les communautés de Lyon + Tech Hub! Rendez-vous sur la page Calendrier. +

+

Cotisez

+

+ Soutenez-nous en cotisant à partir de 1€ par an seulement. +

+
+
-

- Lyon Tech Hub est un groupe informel constitué des membres des différentes communautés IT/tech lyonnaises. - Le groupe est auto-géré par les membres les plus actifs. -

- -

- Les membres sont des passionnés, des experts, des curieux, des professionnels de tous horizons: - éditeurs, sociétés industrielles ou commerciales, startups, indépendants, sociétés de service... - Ils sont réunis par la volonté d'apprendre et d'échanger. -

-
-
-
- -
-
-
-

Rejoignez nous !

-

Sur Slack

-

Pour discuter avec les membres de nos communautés sur Slack, inscrivez-vous sur la page : https://slack.lyontechhub.org.

-

En vrai

-

- N'hésitez pas à participer aux évenements organisés par les communautés de Lyon Tech Hub ! Rendez-vous - sur la page Calendrier. -

-

Côtisez

-

- Soutenez-nous en côtisant à partir de 1€: par - assoconnect. -

- -

Référencer une communauté

-

- Pour inscrire votre communauté, envoyez une pull request sur GitHub pour mettre en ligne les - informations concernant votre communauté. -

    -
  • Forkez le repository du projet. Pour plus - d'informations sur les pull requests, consultez l'aide de Github. -
  • -
  • - Ajoutez les détails de votre communauté dans le fichier - data/[key].json - utilisée dans le fichier précédent -
  • -
  • - Ajoutez le logo de votre communauté au format PNG dans le répertoire imgs, nommé - [key].png - (100px de haut au minimum, pas trop volumineuse) -
  • -
  • Proposez la pull request, elle sera déployée après validation.
  • -
-

-
+
+
+ +

Référencer une communauté

+

+ Pour inscrire votre communauté, envoyez une pull request sur GitHub pour mettre + en ligne les informations concernant votre communauté. +

+
    +
  • Forkez le repository du projet. + Pour plus d'informations sur les pull requests, consultez + l'aide de Github. +
  • +
  • + Ajoutez les détails de votre communauté dans le fichier + data/[key].json +
  • +
  • + Ajoutez le logo de votre communauté au format PNG dans le répertoire + public/imgs/communities/, + nommé [key].png (100px de haut au minimum) +
  • +
  • Proposez la pull request, elle sera déployée après validation.
  • +
+
+
+
+
-
-
-
-
-

Des questions ?

-

Contactez nous -

-

-
+
+
+ +

Des questions ?

+

Contactez-nous

+ +
-
-
-
+
diff --git a/src/pages/calendar.astro b/src/pages/calendar.astro index 8ad981b..dcdf4d3 100644 --- a/src/pages/calendar.astro +++ b/src/pages/calendar.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/Layout.astro';
-
+

Le calendrier partagé par toutes les communautés lyonnaises @@ -12,23 +12,43 @@ import Layout from '../layouts/Layout.astro';

Venez rencontrer toutes ces communautés et leurs participants !

-

Ajouter le calendrier des nouveaux événements à vos calendriers +

Ajouter le calendrier des nouveaux événements à vos calendriers au format ical.

- + + +
+
diff --git a/src/pages/communities.astro b/src/pages/communities.astro index ff3421f..e5f8f7b 100644 --- a/src/pages/communities.astro +++ b/src/pages/communities.astro @@ -1,53 +1,44 @@ --- import Layout from '../layouts/Layout.astro'; -import { getList } from '../lib/communities.ts'; +import {getList} from '../lib/communities.ts'; const communities = getList(); - --- -
-
-

- {communities.length} communautés diverses et variées regroupées dans Lyon Tech Hub -

-

- Par ici pour référencer la votre ! -

-
- -
- -
-
-
- {communities - .map( - (community) => ( - - ) - ) - } +
+
+
+

+ {communities.length} communautés variées regroupées dans Lyon Tech Hub +

+ +
+ {communities + .map( + (community) => ( + + +

{community.name}

+ {community.name}En savoir plus +
+ + ) + ) + } +
+
-
-
+
diff --git a/src/pages/community/[key].astro b/src/pages/community/[key].astro index 9ff720d..3cd9721 100644 --- a/src/pages/community/[key].astro +++ b/src/pages/community/[key].astro @@ -20,14 +20,11 @@ const { community } = Astro.props;
-
-

+

-

{community.name}

@@ -35,7 +32,7 @@ const { community } = Astro.props; id="communityDetails" data-patterns-google-calendar={JSON.stringify(community.patternsGoogleCalendar)}>
-
+

En bref...

{community.shortDescription}

@@ -45,7 +42,7 @@ const { community } = Astro.props; const displayData = getSocialDisplayData(socialLink) return ( - + diff --git a/src/pages/conferences.astro b/src/pages/conferences.astro index 3dddebb..ffe507e 100644 --- a/src/pages/conferences.astro +++ b/src/pages/conferences.astro @@ -16,45 +16,33 @@ const conferences: Conference[] = globFiles['../../data/conferences.json'].defau
-
+

{conferences.length} conférences organisées par les communautés du LyonTechHub

-

- Par ici pour référencer la votre ! -

-
- -
- -
+ -
+
{conferences .map( (conference) => ( -
-
-
- -

{conference.name}

-
-
- -
-
- {conference.shortDescription} -
-
-
-
+ +

{conference.name}

+ {conference.name} +

{conference.shortDescription}

+ En savoir plus +
) ) } diff --git a/src/pages/index.astro b/src/pages/index.astro index e007c09..f202641 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -3,72 +3,60 @@ import Layout from '../layouts/Layout.astro'; --- -
-
diff --git a/src/pages/member.astro b/src/pages/member.astro index 0979ace..fd1ae5c 100644 --- a/src/pages/member.astro +++ b/src/pages/member.astro @@ -2,72 +2,80 @@ import Layout from '../layouts/Layout.astro'; --- -
-
-
-

Vous êtes membre d'une communauté

-
-
-
-
-
- -

Slack

-

Rejoignez le slack pour continuer à échanger entre les évènements de vos communautés, et mettre en avant vos prochains événements. Inscrivez vous : https://slack.lyontechhub.org/

+
+
+ -
-
-
- -

Soirée des Communautés

-

Ne manquez pas notre événement annuel en juin, la Soirée des Communautés, où vous pourrez rencontrer d'autres organisateurs, échanger des conseils et résoudre des problèmes ensemble. C'est l'occasion idéale pour découvrir de nouvelles astuces et partager vos expériences.

+
+
+ +

Soirée des Communautés

+

Ne manquez pas notre événement annuel en juin, la Soirée des + Communautés, où vous pourrez rencontrer d'autres organisateurs, échanger des conseils et + résoudre des problèmes ensemble. C'est l'occasion idéale pour découvrir de nouvelles + astuces + et partager vos expériences.

+
-
-
-

Pour plus d'opportunités de...

-
-
-
-
-
- -

Rencontrer

-

Les événements sont une très bonne occasion pour rencontrer des experts pour vous aider dans vos projets, de potentiels clients, de futurs ou anciens collègues, ou pour le plaisir !

+
+

Pour plus d'opportunités de...

+
+
+
+ +

Rencontrer

+

Les événements sont une très bonne occasion pour rencontrer des + experts pour vous aider dans + vos projets, de potentiels clients, de futurs ou anciens collègues, ou pour le plaisir + !

+
-
-
-
-
-
- -

Découvrir

-

Vous trouverez toujours un sujet à découvrir pour en faire une opportunité de faire mieux ou tout simplement autrement

+
+
+ +

Découvrir

+

Vous trouverez toujours un sujet à découvrir pour en faire une + opportunité de faire mieux + ou tout simplement autrement.

+
-
-
-
-
-
- -

Apprendre

-

Vous apprendrez des expériences des autres, vous serez motivé pour aller plus loin suite à la découverte d'un sujet, vous rencontrerez des formateurs reconnus...

+
+
+ +

Apprendre

+

Vous apprendrez des expériences des autres, vous serez motivé pour + aller plus loin suite + à la découverte d'un sujet, vous rencontrerez des formateurs reconnus...

+
-
-
-
-
-
- -

Echanger

-

La richesse et la diversité des échanges vous apporteront de nouvelles perspectives et des idées pour le futur

+
+
+ +

Echanger

+

La richesse et la diversité des échanges vous apporteront de + nouvelles perspectives et + des idées pour le futur.

+
-
-
+
diff --git a/src/pages/orga.astro b/src/pages/orga.astro index cbda8a8..7d1525b 100644 --- a/src/pages/orga.astro +++ b/src/pages/orga.astro @@ -3,83 +3,87 @@ import Layout from '../layouts/Layout.astro'; ---
-
-
+
-

Vous organisez une communauté

-
-
-
-
-
- -

Assurance Événementielle

-

Organisez vos événements en toute tranquillité grâce à notre assurance couvrant une gamme de risques. Sont notamment couverts : responsabilité civile de l'occupant, reponsabilité civile "produits" (y compris risque d'intoxication alimentaire), dommage aux biens, etc.

-

Comment en bénéficier ? Il suffit qu'un participant à votre évènement ait payé la cotisation de 1€ à Lyon Tech Hub par assoconnect.

-
+

Vous organisez une communauté

+ -
-
-
- -

Espace de Stockage Nextcloud

-

Simplifiez le partage de fichiers et la collaboration dans votre communauté grâce à un espace de stockage Nextcloud dédié à votre communauté. Contactez-nous pour en bénéficier via X (Twitter) ou contact@lyontechhub.org.

-
+ -
-
-
-
- -

Soirée des Communautés

-

Ne manquez pas notre événement annuel en juin, la Soirée des Communautés, où vous pourrez rencontrer d'autres organisateurs, échanger des conseils et résoudre des problèmes ensemble. C'est l'occasion idéale pour découvrir de nouvelles astuces et partager vos expériences.

-
+
-
-
-

Pour plus d'opportunités de...

-
-
-
-
-
- -

Rencontrer

-

Les événements sont une très bonne occasion pour rencontrer des experts pour vous aider dans vos projets, de potentiels clients, de futurs ou anciens collègues, ou pour le plaisir !

+
+

Pour plus d'opportunités de...

+
+
+
+
+ +

Rencontrer

+

Les événements sont une très bonne occasion pour rencontrer + des experts pour vous aider dans vos projets, de potentiels clients, de futurs ou + anciens collègues, ou pour le plaisir !

-
-
-
-
- -

Découvrir

-

Vous trouverez toujours un sujet à découvrir pour en faire une opportunité de faire mieux ou tout simplement autrement.

+
+
+ +

Découvrir

+

Vous trouverez toujours un sujet à découvrir pour en faire une + opportunité de faire mieux ou tout simplement autrement.

-
-
-
-
- -

Apprendre

-

Vous apprendrez des expériences des autres, vous serez motivé pour aller plus loin suite à la découverte d'un sujet, vous rencontrerez des formateurs reconnus...

+
+
+ +

Apprendre

+

Vous apprendrez des expériences des autres, vous serez motivé + pour aller plus loin suite à la découverte d'un sujet, vous rencontrerez des + formateurs reconnus...

-
-
-
-
- -

Échanger

-

La richesse et la diversité des échanges vous apporteront de nouvelles perspectives et des idées pour le futur.

+
+
+ +

Échanger

+

La richesse et la diversité des échanges vous apporteront de + nouvelles perspectives et des idées pour le futur.

-
-
+