Skip to content

Commit 8636584

Browse files
committed
Add index, show and new views for Administrate
1 parent a935a85 commit 8636584

File tree

3 files changed

+330
-0
lines changed

3 files changed

+330
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<%#
2+
# Index
3+
4+
This view is the template for the index page.
5+
It renders the search bar, header and pagination.
6+
It renders the `_table` partial to display details about the resources.
7+
8+
## Local variables:
9+
10+
- `resources`:
11+
An ActiveModel::Relation collection of resources to be displayed in the table.
12+
By default, the number of resources is limited by pagination
13+
or by a hard limit to prevent excessive page load times
14+
%>
15+
16+
<% content_for(:title) { "School Import History" } %>
17+
18+
<header class="main-content__header">
19+
<h1 class="main-content__page-title">
20+
<%= content_for(:title) %>
21+
</h1>
22+
23+
<div>
24+
<%= link_to(
25+
"New Import",
26+
new_admin_school_import_result_path,
27+
class: "button button--primary",
28+
) %>
29+
</div>
30+
</header>
31+
32+
<section class="main-content__body main-content__body--flush">
33+
<%= render(
34+
"collection",
35+
collection_presenter: page,
36+
collection_field_name: :resources,
37+
page: page,
38+
resources: resources,
39+
table_title: "page_title"
40+
) %>
41+
42+
<%= paginate resources, param_name: :_page %>
43+
</section>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<% content_for(:title) { "Upload School Import CSV" } %>
2+
3+
<header class="main-content__header">
4+
<h1 class="main-content__page-title">
5+
<%= content_for(:title) %>
6+
</h1>
7+
<div>
8+
<%= link_to(
9+
"View Import History",
10+
admin_school_import_results_path,
11+
class: "button"
12+
) %>
13+
</div>
14+
</header>
15+
16+
<section class="main-content__body">
17+
<div class="field-unit">
18+
<p>
19+
Use this form to import multiple schools from a CSV file.
20+
The import will run in the background and you can track its progress on the Import History page.
21+
</p>
22+
</div>
23+
24+
<hr style="margin: 2rem 0;">
25+
26+
<%= form_with url: admin_school_import_results_path, method: :post, multipart: true, class: "form" do |f| %>
27+
<div class="field-unit field-unit--file">
28+
<%= f.label :csv_file, "CSV File", class: "field-unit__label" %>
29+
<%= f.file_field :csv_file, accept: ".csv", required: true, class: "field-unit__field" %>
30+
<p class="field-unit__hint">
31+
Select a CSV file containing school data to import.
32+
Download the <%= link_to "CSV template", "/docs/import/school_import_template.csv" %> to see the required format.
33+
</p>
34+
</div>
35+
36+
<div class="form-actions">
37+
<%= f.submit "Upload and Start Import", class: "button button--primary" %>
38+
</div>
39+
<% end %>
40+
41+
<hr style="margin: 2rem 0;">
42+
43+
<div class="field-unit">
44+
<h3>CSV Format Requirements</h3>
45+
<table class="collection-data">
46+
<thead>
47+
<tr>
48+
<th>Column</th>
49+
<th>Required</th>
50+
<th>Description</th>
51+
</tr>
52+
</thead>
53+
<tbody>
54+
<tr>
55+
<td><code>name</code></td>
56+
<td>Yes</td>
57+
<td>School name</td>
58+
</tr>
59+
<tr>
60+
<td><code>website</code></td>
61+
<td>Yes</td>
62+
<td>School website URL (must include https://)</td>
63+
</tr>
64+
<tr>
65+
<td><code>address_line_1</code></td>
66+
<td>Yes</td>
67+
<td>Street address</td>
68+
</tr>
69+
<tr>
70+
<td><code>municipality</code></td>
71+
<td>Yes</td>
72+
<td>City/town</td>
73+
</tr>
74+
<tr>
75+
<td><code>country_code</code></td>
76+
<td>Yes</td>
77+
<td>2-letter ISO country code (e.g., US, GB, CA)</td>
78+
</tr>
79+
<tr>
80+
<td><code>owner_email</code></td>
81+
<td>Yes</td>
82+
<td>Email of school owner (must have existing RPF account)</td>
83+
</tr>
84+
<tr>
85+
<td><code>address_line_2</code></td>
86+
<td>No</td>
87+
<td>Additional address information</td>
88+
</tr>
89+
<tr>
90+
<td><code>administrative_area</code></td>
91+
<td>No</td>
92+
<td>State/province</td>
93+
</tr>
94+
<tr>
95+
<td><code>postal_code</code></td>
96+
<td>No</td>
97+
<td>ZIP/postal code</td>
98+
</tr>
99+
<tr>
100+
<td><code>reference</code></td>
101+
<td>No</td>
102+
<td>School reference number (must be unique if provided)</td>
103+
</tr>
104+
</tbody>
105+
</table>
106+
</div>
107+
108+
<div class="field-unit" style="margin-top: 2rem;">
109+
<h3>Important Notes</h3>
110+
</div>
111+
<div class="field-unit" style="margin-top: 2rem;">
112+
<ul>
113+
<li>All owner emails must correspond to existing Code Editor for Education accounts.</li>
114+
<li>Each owner can only create one school.</li>
115+
<li>The same owner email cannot appear multiple times in the CSV.</li>
116+
<li>Imported schools are automatically verified and receive school codes.</li>
117+
<li>Owner roles are automatically created for each school.</li>
118+
<li>The import runs in the background and may take several minutes for large files.</li>
119+
</ul>
120+
</div>
121+
</section>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<% content_for(:title) { "School Import Job #{page.resource.job_id}" } %>
2+
3+
<header class="main-content__header">
4+
<h1 class="main-content__page-title">
5+
<%= content_for(:title) %>
6+
</h1>
7+
<div>
8+
<%= link_to(
9+
"Download CSV",
10+
admin_school_import_result_path(page.resource, format: :csv),
11+
class: "button button--primary",
12+
style: "margin-right: 10px;"
13+
) %>
14+
<%= link_to(
15+
"Back to Import History",
16+
admin_school_import_results_path,
17+
class: "button"
18+
) %>
19+
</div>
20+
</header>
21+
22+
<section class="main-content__body">
23+
<dl>
24+
<dt class="attribute-label">Job ID</dt>
25+
<dd class="attribute-data"><%= page.resource.job_id %></dd>
26+
27+
<dt class="attribute-label">User</dt>
28+
<dd class="attribute-data">
29+
<% user_field = UserInfoField.new(:user_id, page.resource.user_id, "show") %>
30+
<%= user_field.user_display %>
31+
</dd>
32+
33+
<dt class="attribute-label">Status</dt>
34+
<dd class="attribute-data">
35+
<% status_field = StatusField.new(:job_id, page.resource.job_id, "show") %>
36+
<span class="status-badge <%= status_field.status_class %>">
37+
<%= status_field.job_status.upcase %>
38+
</span>
39+
</dd>
40+
41+
<dt class="attribute-label">Created At</dt>
42+
<dd class="attribute-data"><%= page.resource.created_at.strftime("%B %d, %Y at %I:%M %p") %></dd>
43+
44+
<% if page.resource.updated_at != page.resource.created_at %>
45+
<dt class="attribute-label">Updated At</dt>
46+
<dd class="attribute-data"><%= page.resource.updated_at.strftime("%B %d, %Y at %I:%M %p") %></dd>
47+
<% end %>
48+
</dl>
49+
50+
<% results = page.resource.results %>
51+
<% if results.present? %>
52+
<dl>
53+
<dt class="attribute-label">Results</dt>
54+
<dd class="attribute-data">
55+
<% results_field = ResultsSummaryField.new(:results, page.resource.results, "show") %>
56+
<%= render "fields/results_summary_field/show", field: results_field %>
57+
</dd>
58+
</dl>
59+
60+
<% successful = results['successful'] || [] %>
61+
<% failed = results['failed'] || [] %>
62+
63+
<% if successful.any? %>
64+
<h2 style="margin-top: 2rem;">Successful Imports (<%= successful.count %>)</h2>
65+
<table class="collection-data">
66+
<thead>
67+
<tr>
68+
<th>School Name</th>
69+
<th>School Code</th>
70+
<th>School ID</th>
71+
<th>Owner Email</th>
72+
<th>Actions</th>
73+
</tr>
74+
</thead>
75+
<tbody>
76+
<% successful.each do |school| %>
77+
<tr>
78+
<td><%= school['name'] %></td>
79+
<td><strong><%= school['code'] %></strong></td>
80+
<td><code><%= school['id'] %></code></td>
81+
<td><%= school['owner_email'] %></td>
82+
<td>
83+
<%= link_to "View School", admin_school_path(school['id']), class: "button button--small" if school['id'] %>
84+
</td>
85+
</tr>
86+
<% end %>
87+
</tbody>
88+
</table>
89+
<% end %>
90+
91+
<% if failed.any? %>
92+
<h2 style="margin-top: 2rem;">Failed Imports (<%= failed.count %>)</h2>
93+
<table class="collection-data">
94+
<thead>
95+
<tr>
96+
<th>School Name</th>
97+
<th>Owner Email</th>
98+
<th>Error Code</th>
99+
<th>Error Message</th>
100+
</tr>
101+
</thead>
102+
<tbody>
103+
<% failed.each do |school| %>
104+
<tr>
105+
<td><%= school['name'] %></td>
106+
<td><%= school['owner_email'] %></td>
107+
<td><code><%= school['error_code'] %></code></td>
108+
<td style="color: red;"><%= school['error'] %></td>
109+
</tr>
110+
<% end %>
111+
</tbody>
112+
</table>
113+
114+
<div class="field-unit" style="margin-top: 2rem;">
115+
<h3>Common Error Codes</h3>
116+
<dl>
117+
<dt><code>OWNER_NOT_FOUND</code></dt>
118+
<dd>The owner email doesn't correspond to an existing account. The owner needs to create a Code Editor for Education account first.</dd>
119+
120+
<dt><code>OWNER_ALREADY_CREATOR</code></dt>
121+
<dd>This owner has already created a school. Each person can only create one school.</dd>
122+
123+
<dt><code>SCHOOL_VALIDATION_FAILED</code></dt>
124+
<dd>The school data failed validation. Check the error message for details about which fields are invalid.</dd>
125+
</dl>
126+
</div>
127+
<% end %>
128+
<% else %>
129+
<div class="flash flash-alert">
130+
<p>No results available yet. The job may still be running or may have failed to store results.</p>
131+
</div>
132+
<% end %>
133+
</section>
134+
135+
<style>
136+
.status-badge {
137+
padding: 4px 8px;
138+
border-radius: 4px;
139+
font-weight: bold;
140+
font-size: 0.9em;
141+
}
142+
.status-completed {
143+
background-color: #d4edda;
144+
color: #155724;
145+
}
146+
.status-failed {
147+
background-color: #f8d7da;
148+
color: #721c24;
149+
}
150+
.status-running {
151+
background-color: #fff3cd;
152+
color: #856404;
153+
}
154+
.status-queued {
155+
background-color: #d1ecf1;
156+
color: #0c5460;
157+
}
158+
.status-unknown {
159+
background-color: #e2e3e5;
160+
color: #383d41;
161+
}
162+
.button--small {
163+
padding: 4px 8px;
164+
font-size: 0.875em;
165+
}
166+
</style>

0 commit comments

Comments
 (0)