You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.9 KiB
132 lines
4.9 KiB
{{define "users_content"}}
|
|
<div class="page-header">
|
|
<h2>User Imports</h2>
|
|
<p>Upload a CSV to create ServiceTrade users and assign roles in bulk.</p>
|
|
</div>
|
|
|
|
<div class="page-content">
|
|
<div class="content">
|
|
<h3 class="submenu-header">Upload CSV</h3>
|
|
{{if .FlashError}}
|
|
<div class="error-message">{{.FlashError}}</div>
|
|
{{end}}
|
|
{{if .FlashSuccess}}
|
|
<div class="info-message">{{.FlashSuccess}}</div>
|
|
{{end}}
|
|
<form action="/users/upload" method="POST" enctype="multipart/form-data">
|
|
{{if .CSRFField}}{{.CSRFField}}{{end}}
|
|
{{if .CSRFCookie}}<input type="hidden" name="csrfToken" value="{{.CSRFCookie}}">{{end}}
|
|
<label for="users-csv">CSV file</label>
|
|
<input id="users-csv" name="csvFile" type="file" accept=".csv" class="card-input" required>
|
|
<button type="submit" class="btn-primary">Process CSV</button>
|
|
</form>
|
|
<div class="info-message" style="margin-top: 1rem;">
|
|
<strong>Required headers:</strong> username, firstName, lastName, email, password, companyId, locationId.
|
|
Optional headers include phone, status, timezone, details, isSales, managerId, mfaRequired, serviceLineIds, and roles.
|
|
In the roles column list the human-readable role names (comma, semicolon, or pipe separated); names are matched case-insensitively.
|
|
</div>
|
|
</div>
|
|
|
|
{{if .RolesError}}
|
|
<div class="content" style="margin-top:1rem;">
|
|
<div class="error-message">{{.RolesError}}</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Roles}}
|
|
<div class="content" style="margin-top:1rem;">
|
|
<h3 class="submenu-header">Available Roles</h3>
|
|
<p>Use the role names from this list in the CSV roles column.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Name</th>
|
|
<th>Description</th>
|
|
<th>Global</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .Roles}}
|
|
<tr>
|
|
<td>{{.ID}}</td>
|
|
<td>{{.Name}}</td>
|
|
<td>{{.Description}}</td>
|
|
<td>{{if .Global}}Yes{{else}}No{{end}}</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .ImportSummary}}
|
|
<div class="content" style="margin-top:1rem;">
|
|
<h3 class="submenu-header">Import Summary</h3>
|
|
<p>
|
|
Processed file: <strong>{{.ImportSummary.ProcessedFilename}}</strong> at {{.ImportSummary.ProcessedAt.Format "2006-01-02 15:04 MST"}}.
|
|
</p>
|
|
<ul>
|
|
<li>Total rows: {{.ImportSummary.TotalRows}}</li>
|
|
<li>Users created: {{.ImportSummary.UsersCreated}}</li>
|
|
<li>Rows failed: {{.ImportSummary.RowsFailed}}</li>
|
|
<li>Role assignments succeeded: {{.ImportSummary.RoleAssignments}}</li>
|
|
<li>Role assignments failed: {{.ImportSummary.RoleAssignmentErrors}}</li>
|
|
</ul>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .ImportResults}}
|
|
<div class="content" style="margin-top:1rem;">
|
|
<h3 class="submenu-header">Import Details</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Row</th>
|
|
<th>Username</th>
|
|
<th>Email</th>
|
|
<th>User Result</th>
|
|
<th>Role Assignments</th>
|
|
<th>Processing Time</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .ImportResults}}
|
|
<tr>
|
|
<td>{{.Row}}</td>
|
|
<td>{{.Username}}</td>
|
|
<td>{{.Email}}</td>
|
|
<td>
|
|
{{if .Error}}
|
|
<div class="error-message">{{.Error}}</div>
|
|
{{else if .Created}}
|
|
Created (#{{.UserID}})
|
|
{{else}}
|
|
Skipped
|
|
{{end}}
|
|
</td>
|
|
<td>
|
|
{{if .RoleAssignments}}
|
|
<ul>
|
|
{{range .RoleAssignments}}
|
|
<li>
|
|
{{if .Success}}✅{{else}}⚠️{{end}}
|
|
{{if .Role}}{{.Role}} ({{.RoleID}}){{else}}{{.Token}}{{end}} — {{.Message}}
|
|
</li>
|
|
{{end}}
|
|
</ul>
|
|
{{else if .Error}}
|
|
—
|
|
{{else}}
|
|
No roles provided
|
|
{{end}}
|
|
</td>
|
|
<td>{{.ProcessingTime}}</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
|