|
78 | 78 | return link; |
79 | 79 | }; |
80 | 80 | const safeHref = (href) => /^(https?:|mailto:|#)/i.test(href || '') ? href : '#'; |
| 81 | + const safeImageSrc = (src) => /^(https?:|assets\/)/i.test(src || '') ? src : ''; |
81 | 82 | const profileUrl = (network, value) => { |
82 | 83 | const raw = String(value || '').trim(); |
83 | 84 | if (!raw) return ''; |
|
137 | 138 | [ |
138 | 139 | ['comunidade', text('community')], ['conheca-tambem', text('partners')], ['membros', text('members')], |
139 | 140 | ['eventos', text('events')], ['projetos', text('projects')], ['blog', text('blog')], ['conduta', text('conduct')] |
140 | | - ].filter(([id]) => (id !== 'blog' || state.posts.length) && (id !== 'membros' || state.members.length)) |
| 141 | + ].filter(([id]) => (id !== 'blog' || state.posts.length) |
| 142 | + && (id !== 'membros' || state.members.length) |
| 143 | + && (id !== 'projetos' || state.projects.length)) |
141 | 144 | .forEach(([id, label]) => nav.append(element('a', { href: `#${id}`, text: label, 'data-section': id }))); |
142 | 145 | } |
143 | 146 |
|
|
158 | 161 |
|
159 | 162 | const cards = byId('home-cards'); |
160 | 163 | clear(cards); |
161 | | - (site.home.cards || []).filter((card) => card.route !== 'membros' || state.members.length).forEach((card, index) => { |
| 164 | + (site.home.cards || []).filter((card) => (card.route !== 'membros' || state.members.length) |
| 165 | + && (card.route !== 'projetos' || state.projects.length)).forEach((card, index) => { |
162 | 166 | const cardLink = element('a', { class: 'card', href: `#${card.route}` }); |
163 | 167 | cardLink.append( |
164 | 168 | element('span', { class: 'card-index', text: String(index + 1).padStart(2, '0') }), |
|
263 | 267 | }); |
264 | 268 | } |
265 | 269 |
|
266 | | - function renderProjectsAndConduct() { |
| 270 | + function renderProjects() { |
| 271 | + const section = byId('projetos'); |
267 | 272 | const projects = byId('project-cards'); |
268 | 273 | clear(projects); |
269 | | - (state.site.projects || []).forEach((project) => { |
270 | | - projects.append(element('article', { class: 'card' }, [element('h3', { text: project.title }), element('p', { text: pick(project.text) })])); |
| 274 | + section.hidden = !state.projects.length; |
| 275 | + if (!state.projects.length) return; |
| 276 | + state.projects.forEach((project) => { |
| 277 | + const card = project.url ? external(project.url, '', 'card') : element('article', { class: 'card' }); |
| 278 | + const imageSrc = safeImageSrc(project.image); |
| 279 | + if (imageSrc) card.append(element('img', { class: 'project-image', src: imageSrc, alt: '' })); |
| 280 | + card.append(element('h3', { text: pick(project.name) }), element('p', { text: pick(project.description) })); |
| 281 | + projects.append(card); |
271 | 282 | }); |
| 283 | + } |
| 284 | + |
| 285 | + function renderConduct() { |
272 | 286 | const conduct = byId('conduct-list'); |
273 | 287 | clear(conduct); |
274 | 288 | (state.site.conduct.items || []).forEach((item) => conduct.append(element('li', { text: pick(item) }))); |
|
312 | 326 | const upcoming = events.filter(isUpcoming).sort((a, b) => a.date - b.date); |
313 | 327 | const past = events.filter((event) => !isUpcoming(event)).sort((a, b) => b.date - a.date); |
314 | 328 | renderNavigation(); renderHome(upcoming); renderCommunity(); renderPartners(); renderMembers(); |
315 | | - renderEvents(upcoming, past); renderProjectsAndConduct(); renderPosts(); renderFooter(); observeSections(); |
| 329 | + renderEvents(upcoming, past); renderProjects(); renderConduct(); renderPosts(); renderFooter(); observeSections(); |
316 | 330 | } |
317 | 331 |
|
318 | 332 | let observer; |
|
327 | 341 | } |
328 | 342 |
|
329 | 343 | async function load() { |
330 | | - const files = ['site.json', 'members.json', 'events.json', 'posts.json']; |
| 344 | + const files = ['site.json', 'members.json', 'events.json', 'projects.json', 'posts.json']; |
331 | 345 | try { |
332 | 346 | const results = await Promise.all(files.map(async (file) => { |
333 | 347 | const response = await fetch(`data/${file}`); |
334 | 348 | if (!response.ok) throw new Error(`${file}: ${response.status}`); |
335 | 349 | return response.json(); |
336 | 350 | })); |
337 | | - [state.site, state.members, state.events, state.posts] = results; |
| 351 | + [state.site, state.members, state.events, state.projects, state.posts] = results; |
338 | 352 | state.members = Array.isArray(state.members) ? state.members : []; |
339 | 353 | state.events = Array.isArray(state.events) ? state.events : []; |
| 354 | + state.projects = Array.isArray(state.projects) ? state.projects : []; |
340 | 355 | state.posts = Array.isArray(state.posts) ? state.posts : []; |
341 | 356 | render(); |
342 | 357 | } catch (error) { |
|
0 commit comments