tiffank1802 commited on
Commit
a78483d
·
1 Parent(s): 8fc4030

Update design: Google Sites style with header, hero, cards and file list

Browse files
src/App.jsx CHANGED
@@ -1,11 +1,25 @@
1
- import ModulePage from './components/ModulePage';
2
-
3
  function App() {
4
  const moduleCode = '4A-S7-ET';
5
 
6
  return (
7
  <div className="app">
8
- <ModulePage moduleCode={moduleCode} />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </div>
10
  );
11
  }
 
 
 
1
  function App() {
2
  const moduleCode = '4A-S7-ET';
3
 
4
  return (
5
  <div className="app">
6
+ <header className="site-header">
7
+ <div className="site-logo">
8
+ <div className="logo-icon">E</div>
9
+ <h1>Énergétique - Thermique</h1>
10
+ </div>
11
+ <nav className="site-nav">
12
+ <a href="#accueil">Accueil</a>
13
+ <a href="#documents">Documents</a>
14
+ <a href="#ressources">Ressources</a>
15
+ </nav>
16
+ </header>
17
+ <main>
18
+ <ModulePage moduleCode={moduleCode} />
19
+ </main>
20
+ <footer className="site-footer">
21
+ <p>ENISE - École Nationale d'Ingénieurs de Saint-Étienne</p>
22
+ </footer>
23
  </div>
24
  );
25
  }
src/components/ModulePage.jsx CHANGED
@@ -46,11 +46,11 @@ export default function ModulePage({ moduleCode }) {
46
 
47
  return (
48
  <div className="module-page">
49
- <header className="module-header">
50
  <h1>{module.title}</h1>
51
- <p className="module-meta">{module.year}A - {module.semester}</p>
52
- <p className="module-description">{module.description}</p>
53
- </header>
54
  <SectionList module={module} sections={sections} />
55
  </div>
56
  );
 
46
 
47
  return (
48
  <div className="module-page">
49
+ <section className="hero-section">
50
  <h1>{module.title}</h1>
51
+ <p className="hero-meta">{module.year}A - {module.semester}</p>
52
+ <p className="hero-description">{module.description}</p>
53
+ </section>
54
  <SectionList module={module} sections={sections} />
55
  </div>
56
  );
src/components/ResourceList.jsx CHANGED
@@ -1,7 +1,7 @@
1
  import { useEffect, useState } from 'react';
2
  import { databases, Query, CONFIG } from '../appwrite';
3
 
4
- export default function ResourceList({ moduleId, sectionId, sectionTitle }) {
5
  const [resources, setResources] = useState([]);
6
  const [loading, setLoading] = useState(true);
7
 
@@ -31,41 +31,50 @@ export default function ResourceList({ moduleId, sectionId, sectionTitle }) {
31
 
32
  if (loading) return <div className="loading">Chargement des ressources...</div>;
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  return (
35
- <div className="resources-container">
36
- <h2>{sectionTitle}</h2>
37
  {resources.length === 0 ? (
38
- <p className="no-resources">Aucune ressource pour cette section.</p>
39
  ) : (
40
- <ul className="resources-list">
41
- {resources.map((r) => (
42
- <li key={r.$id} className="resource-item">
43
- <a
44
- href={r.url}
45
- target="_blank"
46
- rel="noopener noreferrer"
47
- className="resource-link"
48
- >
49
- <span className="resource-icon">{getIcon(r.type)}</span>
50
- <span className="resource-title">{r.title}</span>
51
- </a>
52
  {r.description && (
53
- <p className="resource-description">{r.description}</p>
54
  )}
55
- </li>
56
- ))}
57
- </ul>
 
58
  )}
59
  </div>
60
  );
61
- }
62
-
63
- function getIcon(type) {
64
- switch (type) {
65
- case 'pdf': return '📄';
66
- case 'video': return '🎬';
67
- case 'link': return '🔗';
68
- case 'image': return '🖼️';
69
- default: return '📎';
70
- }
71
  }
 
1
  import { useEffect, useState } from 'react';
2
  import { databases, Query, CONFIG } from '../appwrite';
3
 
4
+ export default function ResourceList({ moduleId, sectionId }) {
5
  const [resources, setResources] = useState([]);
6
  const [loading, setLoading] = useState(true);
7
 
 
31
 
32
  if (loading) return <div className="loading">Chargement des ressources...</div>;
33
 
34
+ const getFileIcon = (type) => {
35
+ switch (type) {
36
+ case 'pdf': return '📄';
37
+ case 'video': return '🎬';
38
+ case 'link': return '🔗';
39
+ case 'image': return '🖼️';
40
+ case 'ppt':
41
+ case 'pptx': return '📊';
42
+ case 'doc':
43
+ case 'docx': return '📝';
44
+ case 'xls':
45
+ case 'xlsx': return '📈';
46
+ default: return '📎';
47
+ }
48
+ };
49
+
50
+ const getFileSize = (url) => {
51
+ return '';
52
+ };
53
+
54
  return (
55
+ <div className="file-list">
 
56
  {resources.length === 0 ? (
57
+ <p className="no-resources">Aucun document disponible pour cette section.</p>
58
  ) : (
59
+ resources.map((r) => (
60
+ <a
61
+ key={r.$id}
62
+ href={r.url}
63
+ target="_blank"
64
+ rel="noopener noreferrer"
65
+ className="file-item"
66
+ >
67
+ <div className="file-icon">{getFileIcon(r.type)}</div>
68
+ <div className="file-info">
69
+ <div className="file-name">{r.title}</div>
 
70
  {r.description && (
71
+ <div className="file-meta">{r.description}</div>
72
  )}
73
+ </div>
74
+ <div className="file-download">↓</div>
75
+ </a>
76
+ ))
77
  )}
78
  </div>
79
  );
 
 
 
 
 
 
 
 
 
 
80
  }
src/components/SectionList.jsx CHANGED
@@ -1,6 +1,16 @@
1
  import { useState } from 'react';
2
  import ResourceList from './ResourceList';
3
 
 
 
 
 
 
 
 
 
 
 
4
  export default function SectionList({ module, sections }) {
5
  const [activeSectionId, setActiveSectionId] = useState(
6
  sections[0]?.$id || null
@@ -9,31 +19,56 @@ export default function SectionList({ module, sections }) {
9
  const activeSection = sections.find(s => s.$id === activeSectionId);
10
 
11
  return (
12
- <div className="section-layout">
13
- <nav className="section-nav">
14
- <h3>Contents</h3>
15
- <ul className="nav-list">
16
- {sections.map((sec) => (
17
- <li key={sec.$id}>
18
- <button
19
- className={`nav-button ${sec.$id === activeSectionId ? 'active' : ''}`}
20
- onClick={() => setActiveSectionId(sec.$id)}
21
- >
22
- {sec.title}
23
- </button>
24
- </li>
25
- ))}
26
- </ul>
27
- </nav>
28
- <main className="section-content">
29
- {activeSectionId && activeSection && (
30
- <ResourceList
31
- moduleId={module.$id}
32
- sectionId={activeSectionId}
33
- sectionTitle={activeSection.title}
34
- />
35
- )}
36
- </main>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  </div>
38
  );
39
  }
 
1
  import { useState } from 'react';
2
  import ResourceList from './ResourceList';
3
 
4
+ const getIcon = (title) => {
5
+ const t = title.toLowerCase();
6
+ if (t.includes('cours')) return '📚';
7
+ if (t.includes('td')) return '✏️';
8
+ if (t.includes('tp')) return '🔧';
9
+ if (t.includes('projet')) return '🛠️';
10
+ if (t.includes('examen')) return '📝';
11
+ return '📁';
12
+ };
13
+
14
  export default function SectionList({ module, sections }) {
15
  const [activeSectionId, setActiveSectionId] = useState(
16
  sections[0]?.$id || null
 
19
  const activeSection = sections.find(s => s.$id === activeSectionId);
20
 
21
  return (
22
+ <div>
23
+ {sections.length > 0 ? (
24
+ <>
25
+ <section className="content-section">
26
+ <div className="section-container">
27
+ <h2 className="section-title">Contenu du cours</h2>
28
+ <div className="content-grid">
29
+ {sections.map((sec) => (
30
+ <div
31
+ key={sec.$id}
32
+ className="content-card"
33
+ onClick={() => setActiveSectionId(sec.$id)}
34
+ >
35
+ <div className="card-header">
36
+ <div className="card-icon">{getIcon(sec.title)}</div>
37
+ <h3 className="card-title">{sec.title}</h3>
38
+ </div>
39
+ <p className="card-description">
40
+ Cliquez pour voir les documents et ressources
41
+ </p>
42
+ <span className="card-link">
43
+ Accéder →
44
+ </span>
45
+ </div>
46
+ ))}
47
+ </div>
48
+ </div>
49
+ </section>
50
+
51
+ <section className="files-section" id="documents">
52
+ <div className="section-container">
53
+ <h2 className="section-title">
54
+ {activeSection ? activeSection.title : 'Documents'}
55
+ </h2>
56
+ {activeSectionId && (
57
+ <ResourceList
58
+ moduleId={module.$id}
59
+ sectionId={activeSectionId}
60
+ />
61
+ )}
62
+ </div>
63
+ </section>
64
+ </>
65
+ ) : (
66
+ <section className="content-section">
67
+ <div className="section-container">
68
+ <div className="error">Aucune section disponible.</div>
69
+ </div>
70
+ </section>
71
+ )}
72
  </div>
73
  );
74
  }
src/index.css CHANGED
@@ -5,158 +5,262 @@
5
  }
6
 
7
  body {
8
- font-family: 'Google Sans', Roboto, Arial, sans-serif;
9
- background-color: #f8f9fa;
10
- color: #202124;
11
- line-height: 1.6;
 
12
  }
13
 
14
  .app {
15
  min-height: 100vh;
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  .module-page {
19
- max-width: 1100px;
20
  margin: 0 auto;
21
- padding: 40px 24px;
22
  }
23
 
24
- .module-header {
25
- margin-bottom: 40px;
26
- padding-bottom: 24px;
27
- border-bottom: 1px solid #e0e0e0;
 
28
  }
29
 
30
- .module-header h1 {
31
- font-size: 2.5rem;
32
  font-weight: 400;
33
  color: #202124;
34
- margin-bottom: 8px;
 
35
  }
36
 
37
- .module-meta {
38
  font-size: 1rem;
39
  color: #5f6368;
40
  margin-bottom: 16px;
41
  }
42
 
43
- .module-description {
44
  font-size: 1.1rem;
45
  color: #3c4043;
46
- max-width: 800px;
 
47
  }
48
 
49
- .section-layout {
50
- display: flex;
51
- gap: 32px;
52
  }
53
 
54
- .section-nav {
55
- width: 240px;
56
- flex-shrink: 0;
57
  }
58
 
59
- .section-nav h3 {
60
- font-size: 0.75rem;
61
- font-weight: 500;
62
- text-transform: uppercase;
63
- letter-spacing: 0.5px;
64
- color: #5f6368;
65
- margin-bottom: 12px;
66
- padding-left: 12px;
67
  }
68
 
69
- .nav-list {
70
- list-style: none;
 
 
71
  }
72
 
73
- .nav-button {
74
- width: 100%;
75
- text-align: left;
76
- padding: 10px 12px;
77
- border: none;
78
- background: transparent;
79
- font-size: 0.95rem;
80
- color: #3c4043;
81
  cursor: pointer;
82
- border-radius: 0 24px 24px 0;
83
- transition: background-color 0.2s ease;
84
  }
85
 
86
- .nav-button:hover {
87
- background-color: #f1f3f4;
 
88
  }
89
 
90
- .nav-button.active {
91
- background-color: #e8f0fe;
92
- color: #1a73e8;
93
- font-weight: 500;
 
94
  }
95
 
96
- .section-content {
97
- flex: 1;
98
- min-width: 0;
 
 
 
 
 
 
99
  }
100
 
101
- .resources-container h2 {
102
- font-size: 1.5rem;
103
- font-weight: 400;
104
  color: #202124;
105
- margin-bottom: 24px;
106
  }
107
 
108
- .no-resources {
 
109
  color: #5f6368;
110
- font-style: italic;
111
  }
112
 
113
- .resources-list {
114
- list-style: none;
 
 
 
 
 
 
115
  }
116
 
117
- .resource-item {
118
- margin-bottom: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  padding: 16px;
120
- background: white;
 
121
  border-radius: 8px;
122
- box-shadow: 0 1px 3px rgba(60, 64, 67, 0.1);
123
- transition: box-shadow 0.2s ease;
124
  }
125
 
126
- .resource-item:hover {
127
- box-shadow: 0 2px 8px rgba(60, 64, 67, 0.15);
128
  }
129
 
130
- .resource-link {
131
- display: inline-flex;
 
 
 
 
132
  align-items: center;
133
- gap: 12px;
134
- text-decoration: none;
135
- color: #1a73e8;
 
 
 
 
 
 
 
136
  font-weight: 500;
137
- font-size: 1rem;
 
138
  }
139
 
140
- .resource-link:hover {
141
- text-decoration: underline;
 
142
  }
143
 
144
- .resource-icon {
 
145
  font-size: 1.25rem;
146
  }
147
 
148
- .resource-description {
149
- margin-top: 8px;
150
- margin-left: 36px;
151
- font-size: 0.9rem;
 
152
  color: #5f6368;
 
153
  }
154
 
155
  .loading {
156
  display: flex;
157
  justify-content: center;
158
  align-items: center;
159
- min-height: 200px;
160
  color: #5f6368;
161
  font-size: 1rem;
162
  }
@@ -168,20 +272,16 @@ body {
168
  }
169
 
170
  @media (max-width: 768px) {
171
- .section-layout {
172
  flex-direction: column;
 
173
  }
174
 
175
- .section-nav {
176
- width: 100%;
177
- }
178
-
179
- .nav-button {
180
- border-radius: 4px;
181
- margin-bottom: 4px;
182
  }
183
 
184
- .module-header h1 {
185
- font-size: 1.75rem;
186
  }
187
  }
 
5
  }
6
 
7
  body {
8
+ font-family: 'Google Sans', 'Roboto', 'Segoe UI', Arial, sans-serif;
9
+ background-color: #fff;
10
+ color: #1a1a1a;
11
+ line-height: 1.5;
12
+ -webkit-font-smoothing: antialiased;
13
  }
14
 
15
  .app {
16
  min-height: 100vh;
17
  }
18
 
19
+ .site-header {
20
+ background: #fff;
21
+ border-bottom: 1px solid #dadce0;
22
+ padding: 16px 24px;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ position: sticky;
27
+ top: 0;
28
+ z-index: 100;
29
+ }
30
+
31
+ .site-logo {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 12px;
35
+ }
36
+
37
+ .site-logo .logo-icon {
38
+ width: 40px;
39
+ height: 40px;
40
+ background: linear-gradient(135deg, #4285f4, #34a853);
41
+ border-radius: 8px;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ color: white;
46
+ font-weight: bold;
47
+ font-size: 18px;
48
+ }
49
+
50
+ .site-logo h1 {
51
+ font-size: 1.25rem;
52
+ font-weight: 500;
53
+ color: #202124;
54
+ }
55
+
56
+ .site-nav {
57
+ display: flex;
58
+ gap: 8px;
59
+ }
60
+
61
+ .site-nav a {
62
+ text-decoration: none;
63
+ color: #5f6368;
64
+ padding: 8px 16px;
65
+ border-radius: 4px;
66
+ font-size: 0.9rem;
67
+ transition: background 0.2s;
68
+ }
69
+
70
+ .site-nav a:hover {
71
+ background: #f1f3f4;
72
+ color: #202124;
73
+ }
74
+
75
  .module-page {
76
+ max-width: 1200px;
77
  margin: 0 auto;
78
+ padding: 0;
79
  }
80
 
81
+ .hero-section {
82
+ background: #f8f9fa;
83
+ padding: 48px 24px;
84
+ text-align: center;
85
+ border-bottom: 1px solid #dadce0;
86
  }
87
 
88
+ .hero-section h1 {
89
+ font-size: 2.75rem;
90
  font-weight: 400;
91
  color: #202124;
92
+ margin-bottom: 12px;
93
+ letter-spacing: -0.5px;
94
  }
95
 
96
+ .hero-meta {
97
  font-size: 1rem;
98
  color: #5f6368;
99
  margin-bottom: 16px;
100
  }
101
 
102
+ .hero-description {
103
  font-size: 1.1rem;
104
  color: #3c4043;
105
+ max-width: 700px;
106
+ margin: 0 auto;
107
  }
108
 
109
+ .content-section {
110
+ padding: 48px 24px;
 
111
  }
112
 
113
+ .section-container {
114
+ max-width: 1100px;
115
+ margin: 0 auto;
116
  }
117
 
118
+ .section-title {
119
+ font-size: 1.75rem;
120
+ font-weight: 400;
121
+ color: #202124;
122
+ margin-bottom: 32px;
123
+ padding-bottom: 16px;
124
+ border-bottom: 1px solid #dadce0;
 
125
  }
126
 
127
+ .content-grid {
128
+ display: grid;
129
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
130
+ gap: 24px;
131
  }
132
 
133
+ .content-card {
134
+ background: #fff;
135
+ border: 1px solid #dadce0;
136
+ border-radius: 8px;
137
+ padding: 24px;
138
+ transition: box-shadow 0.2s, transform 0.2s;
 
 
139
  cursor: pointer;
 
 
140
  }
141
 
142
+ .content-card:hover {
143
+ box-shadow: 0 4px 12px rgba(60, 64, 67, 0.15);
144
+ transform: translateY(-2px);
145
  }
146
 
147
+ .card-header {
148
+ display: flex;
149
+ align-items: center;
150
+ gap: 16px;
151
+ margin-bottom: 16px;
152
  }
153
 
154
+ .card-icon {
155
+ width: 48px;
156
+ height: 48px;
157
+ background: #e8f0fe;
158
+ border-radius: 8px;
159
+ display: flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ font-size: 1.5rem;
163
  }
164
 
165
+ .card-title {
166
+ font-size: 1.1rem;
167
+ font-weight: 500;
168
  color: #202124;
 
169
  }
170
 
171
+ .card-description {
172
+ font-size: 0.9rem;
173
  color: #5f6368;
174
+ margin-bottom: 16px;
175
  }
176
 
177
+ .card-link {
178
+ display: inline-flex;
179
+ align-items: center;
180
+ gap: 4px;
181
+ color: #1a73e8;
182
+ text-decoration: none;
183
+ font-size: 0.9rem;
184
+ font-weight: 500;
185
  }
186
 
187
+ .card-link:hover {
188
+ text-decoration: underline;
189
+ }
190
+
191
+ .files-section {
192
+ background: #f8f9fa;
193
+ padding: 48px 24px;
194
+ }
195
+
196
+ .file-list {
197
+ display: flex;
198
+ flex-direction: column;
199
+ gap: 12px;
200
+ }
201
+
202
+ .file-item {
203
+ display: flex;
204
+ align-items: center;
205
+ gap: 16px;
206
  padding: 16px;
207
+ background: #fff;
208
+ border: 1px solid #dadce0;
209
  border-radius: 8px;
210
+ text-decoration: none;
211
+ transition: box-shadow 0.2s;
212
  }
213
 
214
+ .file-item:hover {
215
+ box-shadow: 0 2px 8px rgba(60, 64, 67, 0.1);
216
  }
217
 
218
+ .file-icon {
219
+ width: 40px;
220
+ height: 40px;
221
+ background: #fef7e0;
222
+ border-radius: 4px;
223
+ display: flex;
224
  align-items: center;
225
+ justify-content: center;
226
+ font-size: 1.25rem;
227
+ }
228
+
229
+ .file-info {
230
+ flex: 1;
231
+ }
232
+
233
+ .file-name {
234
+ font-size: 0.95rem;
235
  font-weight: 500;
236
+ color: #202124;
237
+ margin-bottom: 4px;
238
  }
239
 
240
+ .file-meta {
241
+ font-size: 0.8rem;
242
+ color: #5f6368;
243
  }
244
 
245
+ .file-download {
246
+ color: #5f6368;
247
  font-size: 1.25rem;
248
  }
249
 
250
+ .site-footer {
251
+ background: #f8f9fa;
252
+ border-top: 1px solid #dadce0;
253
+ padding: 24px;
254
+ text-align: center;
255
  color: #5f6368;
256
+ font-size: 0.85rem;
257
  }
258
 
259
  .loading {
260
  display: flex;
261
  justify-content: center;
262
  align-items: center;
263
+ min-height: 300px;
264
  color: #5f6368;
265
  font-size: 1rem;
266
  }
 
272
  }
273
 
274
  @media (max-width: 768px) {
275
+ .site-header {
276
  flex-direction: column;
277
+ gap: 16px;
278
  }
279
 
280
+ .hero-section h1 {
281
+ font-size: 2rem;
 
 
 
 
 
282
  }
283
 
284
+ .content-grid {
285
+ grid-template-columns: 1fr;
286
  }
287
  }