11---
22const { textos, iniciativas, projetos = [] } = Astro .props ;
3+
4+ // Usar os dados do JSON fornecido
5+ const projetosData = iniciativas ?.projetosLista || projetos || [];
6+
7+ console .log (" Total de projetos:" , projetosData .length );
8+ console .log (" Estrutura do primeiro projeto:" , Object .keys (projetosData [0 ] || {}));
9+
10+ // Obter parâmetro de ordenação da URL
11+ const url = new URL (Astro .request .url );
12+ const ordemValida = [" titulo" , " docente" , " periodo" ];
13+ const ordemParam = url .searchParams .get (" ordem" ) ?? " " ;
14+
15+ console .log (" Parâmetro 'ordem' da URL:" , ordemParam );
16+
17+ // Determinar ordem final
18+ const ordem = ordemValida .includes (ordemParam )? ordemParam : " titulo" ;
19+ console .log (" Ordem selecionada:" , ordem );
20+
21+ const getOrdenado = (ordem : string ) => {
22+ return [... projetosData ].sort ((a , b ) => {
23+ const valorA = (a [ordem ] ?? " " ).toString ().trim ().toLowerCase ();
24+ const valorB = (b [ordem ] ?? " " ).toString ().trim ().toLowerCase ();
25+
26+ if (ordem === " periodo" ) {
27+ const anoA = parseInt (valorA .match (/ (\d {4} )/ )?.[1 ] || " 0" );
28+ const anoB = parseInt (valorB .match (/ (\d {4} )/ )?.[1 ] || " 0" );
29+ return anoA - anoB ;
30+ }
31+
32+ return valorA .localeCompare (valorB , ' pt-BR' , { sensitivity: ' base' });
33+ });
34+ }
35+ const projetosOrdenados = getOrdenado (ordem );
36+
37+ // Função para escapar HTML
38+ const escapeHtml = (text ) => {
39+ if (! text ) return ' ' ;
40+ return text .toString ()
41+ .replace (/ &/ g , ' &' )
42+ .replace (/ </ g , ' <' )
43+ .replace (/ >/ g , ' >' )
44+ .replace (/ "/ g , ' "' )
45+ .replace (/ '/ g , ' '' );
46+ };
347---
448
549<section class =" max-w-5xl mx-auto px-4 py-16 space-y-8" >
6- <!-- Título e descrição -->
7- <h1 class =" text-3xl font-bold text-primary" >
8- { iniciativas ?.projetosDePesquisa ?? " Projetos de Pesquisa" }
9- </h1 >
10- <p class =" text-base-content mb-4" >
11- { iniciativas ?.descricaoProjetosDePesquisa ??
12- " Projetos desenvolvidos por docentes e associados ao CPPS, com apoio de agências de fomento." }
13- </p >
14-
15- <!-- Lista de projetos sem ordenação -->
16- <div class =" space-y-4" >
17- { projetos .map ((proj , index ) => (
18- <details class = " border border-base-300 rounded-xl p-4 group" >
19- <summary class = " cursor-pointer text-lg font-semibold text-primary group-open:mb-4" >
20- { proj .titulo } — <span class = " text-base-content" >{ proj .docente } </span >
21- <span class = " text-sm text-neutral-500" >({ proj .periodo } )</span >
22- </summary >
23- <div class = " space-y-2 mt-2 text-base-content" >
24- <p ><strong >Agência:</strong > { proj .agencia } </p >
25- <p ><strong >Processo:</strong > { proj .processo } </p >
26- <p ><strong >Natureza:</strong > { proj .natureza } </p >
27- <p ><strong >Valor:</strong > { proj .valor } </p >
28- <p ><strong >Pesquisadores Associados:</strong > { proj .associados } </p >
29- <p ><strong >Resumo:</strong > { proj .resumo } </p >
30- { Array .isArray (proj .apoioCentro ) && proj .apoioCentro .length > 0 && (
31- <div >
32- <strong >Apoios do CPPS:</strong >
33- <ul class = " list-disc list-inside ml-4" >
34- { proj .apoioCentro .map ((item ) => (
35- <li >{ item } </li >
36- ))}
37- </ul >
50+ <!-- Cabeçalho -->
51+ <div class =" text-center mb-8" >
52+ <h1 class =" text-4xl font-bold text-primary mb-4" >
53+ { iniciativas ?.projetosDePesquisa ?? " Projetos de Pesquisa" }
54+ </h1 >
55+ <p class =" text-lg text-base-content max-w-3xl mx-auto" >
56+ { iniciativas ?.descricaoProjetosDePesquisa ?? " Projetos desenvolvidos por docentes e associados ao CPPS." }
57+ </p >
58+ </div >
59+
60+ <!-- Estatísticas -->
61+ <div class =" stats shadow w-full mb-8" >
62+ <div class =" stat" >
63+ <div class =" stat-title" >Total de Projetos</div >
64+ <div class =" stat-value text-primary" >{ projetosOrdenados .length } </div >
65+ </div >
66+ <div class =" stat" >
67+ <div class =" stat-title" >Ordenação Atual</div >
68+ <div class =" stat-value text-sm" >
69+ { ordem === ' titulo' ? ' Por Título' :
70+ ordem === ' docente' ? ' Por Docente' :
71+ ' Por Período' }
72+ </div >
73+ </div >
74+ </div >
75+
76+ <!-- Seletor de ordenação melhorado -->
77+ <div class =" flex flex-wrap gap-4 items-center justify-between mb-8 p-4 bg-base-200 rounded-lg" >
78+ <div class =" flex items-center gap-2" >
79+ <span class =" font-semibold text-base-content" >
80+ { iniciativas ?.ordenarPor ?? " Ordenar por" } :
81+ </span >
82+ <div class =" btn-group" >
83+ <button
84+ class ={ ` btn btn-sm ${ordem === ' titulo' ? ' btn-active btn-primary' : ' btn-outline' } ` }
85+ onclick =" ordenarProjetos (' titulo' )"
86+ >
87+ { iniciativas ?.porTitulo ?? " Título" }
88+ </button >
89+ <button
90+ class ={ ` btn btn-sm ${ordem === ' docente' ? ' btn-active btn-primary' : ' btn-outline' } ` }
91+ onclick =" ordenarProjetos (' docente' )"
92+ >
93+ { iniciativas ?.porDocente ?? " Docente" }
94+ </button >
95+ <button
96+ class ={ ` btn btn-sm ${ordem === ' periodo' ? ' btn-active btn-primary' : ' btn-outline' } ` }
97+ onclick =" ordenarProjetos (' periodo' )"
98+ >
99+ { iniciativas ?.porPeriodo ?? " Período" }
100+ </button >
101+ </div >
102+ </div >
103+
104+ <!-- Filtro adicional -->
105+ <div class =" flex items-center gap-2" >
106+ <span class =" font-semibold text-base-content" >Filtrar:</span >
107+ <select class =" select select-bordered select-sm" id =" filtro-agencia" >
108+ <option value =" " >Todas as Agências</option >
109+ <option value =" FAPESP" >FAPESP</option >
110+ <option value =" CNPq" >CNPq</option >
111+ <option value =" CAPES" >CAPES</option >
112+ </select >
113+ </div >
114+ </div >
115+
116+ <!-- Lista de projetos melhorada -->
117+ <div id =" lista-projetos" class =" space-y-6" >
118+ { projetosOrdenados .map ((proj , index ) => (
119+ <article class = " card bg-base-100 shadow-lg border border-base-300 hover:shadow-xl transition-shadow duration-300" key = { index } >
120+ <div class = " card-body" >
121+ <!-- Cabeçalho do projeto -->
122+ <div class = " flex flex-wrap items-start justify-between gap-4 mb-4" >
123+ <div class = " flex-1" >
124+ <div class = " flex items-center gap-2 mb-2" >
125+ <span class = " badge badge-primary badge-sm" >#{ index + 1 } </span >
126+ <span class = " badge badge-outline badge-sm" >{ proj .agencia } </span >
127+ <span class = " badge badge-ghost badge-sm" >{ proj .periodo } </span >
128+ </div >
129+ <h2 class = " card-title text-xl text-primary mb-2" >
130+ { proj .titulo }
131+ </h2 >
132+ <p class = " text-base-content font-medium" >
133+ <span class = " text-sm text-neutral-500" >Docente responsável:</span > { proj .docente }
134+ </p >
135+ </div >
136+ <button class = " btn btn-circle btn-ghost btn-sm" onclick = { ` toggleDetails('projeto-${index }') ` } >
137+ <svg class = " w-5 h-5" fill = " none" stroke = " currentColor" viewBox = " 0 0 24 24" >
138+ <path stroke-linecap = " round" stroke-linejoin = " round" stroke-width = " 2" d = " M19 9l-7 7-7-7" ></path >
139+ </svg >
140+ </button >
141+ </div >
142+
143+ <!-- Resumo sempre visível -->
144+ <div class = " mb-4" >
145+ <h3 class = " font-semibold text-base-content mb-2" >Resumo:</h3 >
146+ <p class = " text-sm text-base-content/80 line-clamp-3" >
147+ { proj .resumo }
148+ </p >
149+ </div >
150+
151+ <!-- Detalhes expansíveis -->
152+ <div id = { ` projeto-${index } ` } class = " hidden space-y-4 pt-4 border-t border-base-300" >
153+ <div class = " grid md:grid-cols-2 gap-4" >
154+ <div class = " space-y-2" >
155+ <p ><strong class = " text-primary" >Processo:</strong > { proj .processo } </p >
156+ <p ><strong class = " text-primary" >Natureza:</strong > { proj .natureza } </p >
157+ <p ><strong class = " text-primary" >Valor:</strong > { proj .valor } </p >
158+ </div >
159+ <div class = " space-y-2" >
160+ <p ><strong class = " text-primary" >Pesquisadores Associados:</strong > { proj .associados } </p >
161+ </div >
38162 </div >
39- )}
163+
164+ { Array .isArray (proj .apoioCentro ) && proj .apoioCentro .length > 0 && (
165+ <div class = " mt-4" >
166+ <h4 class = " font-semibold text-primary mb-2" >Apoios do CPPS:</h4 >
167+ <ul class = " list-disc list-inside space-y-1 text-sm" >
168+ { proj .apoioCentro .map ((item , i ) => (
169+ <li key = { i } class = " text-base-content/80" >{ item } </li >
170+ ))}
171+ </ul >
172+ </div >
173+ )}
174+ </div >
40175 </div >
41- </details >
176+ </article >
42177 ))}
43178 </div >
179+
180+ <!-- Mensagem quando não há projetos -->
181+ { projetosOrdenados .length === 0 && (
182+ <div class = " text-center py-12" >
183+ <div class = " text-6xl mb-4" >📚</div >
184+ <h3 class = " text-xl font-semibold text-base-content mb-2" >Nenhum projeto encontrado</h3 >
185+ <p class = " text-base-content/60" >Não há projetos disponíveis no momento.</p >
186+ </div >
187+ )}
44188</section >
189+
190+ <!-- Dados para o cliente -->
191+ <script type =" application/json" id =" projetos-data" >
192+ {JSON.stringify(projetosOrdenados)}
193+ </script >
194+
195+ <script is:inline >
196+ // Função para escapar HTML
197+ function escapeHtml(text) {
198+ if (!text) return '';
199+ const div = document.createElement('div');
200+ div.textContent = text;
201+ return div.innerHTML;
202+ }
203+
204+ // Função para alternar detalhes do projeto
205+ function toggleDetails(elementId) {
206+ const element = document.getElementById(elementId);
207+ const button = element?.previousElementSibling?.querySelector('button[onclick*="toggleDetails"]');
208+
209+ if (element) {
210+ element.classList.toggle('hidden');
211+ if (button) {
212+ const svg = button.querySelector('svg');
213+ if (svg) {
214+ svg.style.transform = element.classList.contains('hidden') ? 'rotate(0deg)' : 'rotate(180deg)';
215+ }
216+ }
217+ }
218+ }
219+
220+ // Função para ordenar e recarregar a página
221+ function ordenarProjetos(criterio) {
222+ const url = new URL(window.location);
223+ url.searchParams.set('ordem', criterio);
224+ window.location.href = url.toString(); // 🔁 reload com o novo critério
225+ }
226+
227+ // Função para atualizar visual dos botões (opcional, se quiser manter ativo visualmente)
228+ function atualizarBotoes(criterioAtivo) {
229+ document.querySelectorAll('.btn-group button').forEach(btn => {
230+ const criterio = btn.getAttribute('onclick')?.match(/'([^']+)'/)?.[1];
231+ if (criterio === criterioAtivo) {
232+ btn.className = 'btn btn-sm btn-active btn-primary';
233+ } else {
234+ btn.className = 'btn btn-sm btn-outline';
235+ }
236+ });
237+ }
238+
239+ // Inicialização: destaca o botão correto se houver parâmetro na URL
240+ document.addEventListener('DOMContentLoaded', function() {
241+ const urlParams = new URLSearchParams(window.location.search);
242+ const ordemUrl = urlParams.get('ordem');
243+ const ordemValida = ['titulo', 'docente', 'periodo'];
244+
245+ if (ordemUrl && ordemValida.includes(ordemUrl)) {
246+ atualizarBotoes(ordemUrl);
247+ }
248+ });
249+
250+ // Tornar funções globais
251+ window.toggleDetails = toggleDetails;
252+ window.ordenarProjetos = ordenarProjetos;
253+ </script >
254+
255+
256+ <style >
257+ .line-clamp-3 {
258+ display: -webkit-box;
259+ -webkit-line-clamp: 3;
260+ -webkit-box-orient: vertical;
261+ overflow: hidden;
262+ }
263+ </style >
0 commit comments