Signing in now starts at the sign-in screen and one button that hands the browser to Go Loose, which returns to the welcome page. Nothing redirects to the identity provider on its own any more: that fired on every anonymous visit and read as a redirect loop rather than as a choice to sign in. The password form remains only where the API reports that Go Loose login is not configured for the host, because otherwise the application is unreachable on a bare localhost. A person's picture is carried through from the Go Loose identity and rendered in the account menu and on the profile page, with their initials as the fallback. Two things make that fallback necessary rather than decorative: Go Loose retires a picture key when a picture is replaced, so a link cached in a browser can be dead, and most people have not uploaded one. The URL is deliberately not persisted locally, since a stored key would keep failing after the picture changed. The account menu is a disclosure rather than a hover menu: a real button with aria-expanded, Escape and outside clicks closing it, and focus returning to the trigger. It is not an ARIA menu, because its actions are a link and a button and menuitem roles would strip the link semantics from one of them for no gain. The account URL of the Go Loose identity is resolved on each sign-in and is read from the live identity provider every time, which is also what makes it correct after a picture change. Depends on the Go Loose revision that serves avatar_url through /connect/userinfo, so the picture URL reaches this application at all.
232 lines
7.6 KiB
Go
232 lines
7.6 KiB
Go
package model
|
|
|
|
import "time"
|
|
|
|
type ErrorResponse struct {
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
type LoginRequest struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type LoginResponse struct {
|
|
Token string `json:"token"`
|
|
User User `json:"user"`
|
|
}
|
|
|
|
type User struct {
|
|
ID uint `json:"id"`
|
|
Email string `json:"email"`
|
|
DisplayName string `json:"displayName"`
|
|
Role string `json:"role"`
|
|
// AvatarURL is an absolute, publicly fetchable picture URL served by Go Loose,
|
|
// or an empty string when the person has not uploaded one.
|
|
AvatarURL string `json:"avatarUrl"`
|
|
}
|
|
|
|
type CreateUserRequest struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
DisplayName string `json:"displayName"`
|
|
}
|
|
|
|
type JobRequest struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Seniority string `json:"seniority"`
|
|
Position string `json:"position"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
Labels []string `json:"labels"`
|
|
RequiredSkills []string `json:"requiredSkills"`
|
|
AdditionalSkills []string `json:"additionalSkills"`
|
|
}
|
|
|
|
type JobResponse struct {
|
|
ID uint `json:"id"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Seniority string `json:"seniority"`
|
|
Position string `json:"position"`
|
|
Status string `json:"status"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
Labels []string `json:"labels"`
|
|
RequiredSkills []string `json:"requiredSkills"`
|
|
AdditionalSkills []string `json:"additionalSkills"`
|
|
Questions []QuestionResponse `json:"questions"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type QuestionRequest struct {
|
|
Text string `json:"text"`
|
|
Type string `json:"type"`
|
|
Options []string `json:"options"`
|
|
CodeSnippet string `json:"codeSnippet"`
|
|
ReferenceAnswer string `json:"referenceAnswer"`
|
|
}
|
|
|
|
type QuestionResponse struct {
|
|
ID uint `json:"id"`
|
|
Text string `json:"text"`
|
|
Type string `json:"type"`
|
|
Options []string `json:"options"`
|
|
CodeSnippet string `json:"codeSnippet"`
|
|
ReferenceAnswer string `json:"referenceAnswer"`
|
|
Deprecated bool `json:"deprecated"`
|
|
}
|
|
|
|
type QuestionStatusRequest struct {
|
|
Deprecated bool `json:"deprecated"`
|
|
}
|
|
|
|
type JobStatusRequest struct {
|
|
Status string `json:"status"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
}
|
|
|
|
type InvitationRequest struct {
|
|
ParticipantID uint `json:"participantId"`
|
|
}
|
|
|
|
type ParticipantResponse struct {
|
|
ID uint `json:"id"`
|
|
FirstName string `json:"firstName"`
|
|
LastName string `json:"lastName"`
|
|
Birthday *string `json:"birthday,omitempty"`
|
|
Email string `json:"email"`
|
|
ContactInfo string `json:"contactInfo"`
|
|
PhotoURL string `json:"photoUrl,omitempty"`
|
|
CVFilename string `json:"cvFilename,omitempty"`
|
|
CVURL string `json:"cvUrl,omitempty"`
|
|
Traits []string `json:"traits"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type CandidateResponse struct {
|
|
Participant ParticipantResponse `json:"participant"`
|
|
Score float64 `json:"score"`
|
|
Matched []string `json:"matchedTraits"`
|
|
Missing []string `json:"missingTraits"`
|
|
}
|
|
|
|
type InvitationResponse struct {
|
|
ID uint `json:"id"`
|
|
JobID uint `json:"jobId"`
|
|
ParticipantID uint `json:"participantId"`
|
|
ParticipantName string `json:"participantName"`
|
|
ParticipantEmail string `json:"participantEmail"`
|
|
Token string `json:"token"`
|
|
Status string `json:"status"`
|
|
Outcome string `json:"outcome"`
|
|
ParticipantURL string `json:"participantUrl"`
|
|
AcceptedAt *time.Time `json:"acceptedAt,omitempty"`
|
|
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type InterviewJobResponse struct {
|
|
ID uint `json:"id"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Position string `json:"position"`
|
|
Seniority string `json:"seniority"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
Questions []InterviewQuestionResponse `json:"questions"`
|
|
}
|
|
|
|
type InterviewQuestionResponse struct {
|
|
ID uint `json:"id"`
|
|
Text string `json:"text"`
|
|
Type string `json:"type"`
|
|
Options []string `json:"options"`
|
|
CodeSnippet string `json:"codeSnippet"`
|
|
}
|
|
|
|
type InterviewResponse struct {
|
|
Invitation InvitationResponse `json:"invitation"`
|
|
Job InterviewJobResponse `json:"job"`
|
|
Answers map[uint]string `json:"answers"`
|
|
}
|
|
|
|
type InvitationReviewResponse struct {
|
|
Invitation InvitationResponse `json:"invitation"`
|
|
Job JobResponse `json:"job"`
|
|
Answers map[uint]string `json:"answers"`
|
|
}
|
|
|
|
type AnswerRequest struct {
|
|
Answer string `json:"answer"`
|
|
}
|
|
|
|
type InvitationOutcomeRequest struct {
|
|
Outcome string `json:"outcome"`
|
|
}
|
|
|
|
type CreateScheduledInterviewRequest struct {
|
|
InvitationID uint `json:"invitationId"`
|
|
StartsAt time.Time `json:"startsAt"`
|
|
Location string `json:"location"`
|
|
InterviewerIDs []uint `json:"interviewerIds"`
|
|
SharedDocument string `json:"sharedDocument"`
|
|
}
|
|
|
|
type InterviewStatusRequest struct {
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type InterviewDocumentRequest struct {
|
|
SharedDocument string `json:"sharedDocument"`
|
|
}
|
|
|
|
type InterviewNoteRequest struct {
|
|
Body string `json:"body"`
|
|
}
|
|
|
|
type AttendeeStatusRequest struct {
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type InterviewAttendeeResponse struct {
|
|
UserID uint `json:"userId"`
|
|
DisplayName string `json:"displayName"`
|
|
Email string `json:"email"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type ScheduledInterviewResponse struct {
|
|
ID uint `json:"id"`
|
|
JobID uint `json:"jobId"`
|
|
JobTitle string `json:"jobTitle"`
|
|
ParticipantID uint `json:"participantId"`
|
|
ParticipantName string `json:"participantName"`
|
|
StartsAt time.Time `json:"startsAt"`
|
|
Location string `json:"location"`
|
|
CandidateToken string `json:"candidateToken"`
|
|
CandidateURL string `json:"candidateUrl"`
|
|
SharedDocument string `json:"sharedDocument"`
|
|
Status string `json:"status"`
|
|
Attendees []InterviewAttendeeResponse `json:"attendees"`
|
|
Notes []InterviewNoteResponse `json:"notes"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
UpdatedAt time.Time `json:"updatedAt"`
|
|
}
|
|
|
|
type ParticipantMeetingResponse struct {
|
|
ID uint `json:"id"`
|
|
JobID uint `json:"jobId"`
|
|
JobTitle string `json:"jobTitle"`
|
|
ParticipantName string `json:"participantName"`
|
|
StartsAt time.Time `json:"startsAt"`
|
|
Location string `json:"location"`
|
|
SharedDocument string `json:"sharedDocument"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type InterviewNoteResponse struct {
|
|
ID uint `json:"id"`
|
|
AuthorUserID uint `json:"authorUserId"`
|
|
AuthorName string `json:"authorName"`
|
|
Body string `json:"body"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|