Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Feat/dat 59 admin can unarchive enrollment #1339

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions backend/app/controllers/enrollments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@ def mark_event_as_processed
render json: @enrollment
end

# GET enrollment/1/unarchive
def unarchive
@enrollment = authorize Enrollment.find(params[:id])
if @enrollment.status == "archived"
@enrollment.unarchive!
render json: @enrollment
else
render status: :unprocessable_entity, json: @enrollment.errors
end
end

private

def pundit_params_for(_record)
Expand Down
28 changes: 28 additions & 0 deletions backend/app/models/enrollment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,34 @@ def archive!
events.create!(name: "archive")
end

def unarchive!
included_events = %w[create request_changes submit validate refuse revoke]
last_event = events.where(name: included_events).last

last_event_name = last_event.name

status_to_update =
case last_event_name
when "created"
"draft"
when "submit"
"submitted"
when "request_changes"
"changes_requested"
when "validate"
"validated"
when "refuse"
"refused"
when "revoke"
"revoked"
else
raise "Unexpected last event: #{last_event_name}"
end

update(status: status_to_update)
events.create!(name: "unarchive", enrollment_id: id)
end

def team_members_json
team_members
.to_a.sort_by { |tm| tm.id }
Expand Down
4 changes: 3 additions & 1 deletion backend/app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ class Event < ApplicationRecord

opinion_created
opinion_comment_created

unarchive
].freeze
EVENTS_WITH_COMMENT_AS_EMAIL_BODY = %w[refuse request_changes validate revoke].freeze

belongs_to :enrollment
belongs_to :entity, polymorphic: true, optional: true

belongs_to :user, optional: true
validates :user, presence: true, if: proc { |event| %w[reminder reminder_before_archive archive].exclude?(event.name) }
validates :user, presence: true, if: proc { |event| %w[reminder reminder_before_archive archive unarchive].exclude?(event.name) }

validates :name, presence: true, inclusion: {in: VALID_NAMES}

Expand Down
4 changes: 4 additions & 0 deletions backend/app/policies/enrollment_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ def archive?
record.can_archive_status? && (demandeur_rights || instructor_right || administrator_right)
end

def unarchive?
record.status_archived? && user.is_administrator?
end

def refuse?
record.can_refuse_status? && user.is_instructor?(record.target_api)
end
Expand Down
1 change: 1 addition & 0 deletions backend/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
get :copies
get :next_enrollments
patch :mark_event_as_processed
patch :unarchive

get :email_templates, to: "enrollments_email_templates#index"
end
Expand Down
51 changes: 51 additions & 0 deletions backend/spec/controllers/enrollments/unarchive_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
RSpec.describe EnrollmentsController, "#unarchive", type: :controller do
describe "#unarchive" do
subject do
patch :unarchive, params: {
id: enrollment.id,
enrollment_status: enrollment.status
}
end

let(:administrator) { create(:user, roles: ["administrator", "franceconnect:instructor"]) }

before do
login(administrator)
end

context "when administrator change status from archive to unarchive" do
let(:enrollment) do
create(
:enrollment,
enrollment_status,
:franceconnect
)
end
let(:enrollment_status) { :submitted }

let(:event) {
create(
:event,
name: "submit",
enrollment_id: enrollment.id
)
}

it "is expected to change enrollment status from archive to unarchive" do
enrollment.update(status: "archived")
create(
:event,
name: "archive",
enrollment_id: enrollment.id
)

expect {
# byebug
subject
}.to change { enrollment.reload.status }.to("submitted")

expect(enrollment.events.last.name).to eq("unarchive")
end
end
end
end
4 changes: 4 additions & 0 deletions backend/spec/factories/events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
name { "archive" }
end

trait :unarchive do
name { "unarchive" }
end

trait :opinion_created do
name { "opinion_created" }

Expand Down
4 changes: 4 additions & 0 deletions backend/spec/models/event_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
archive
opinion_created
opinion_comment_created
unarchive
].each do |trait|
expect(build(:event, trait)).to be_valid
end
Expand Down Expand Up @@ -181,6 +182,7 @@
reminder
reminder_before_archive
archive
unarchive
].each do |name|
context "when name is '#{name}'" do
let(:name) { name }
Expand Down Expand Up @@ -239,6 +241,8 @@
%w[
reminder
reminder_before_archive
archive
unarchive
].each do |name|
context "when name '#{name}'" do
let(:name) { name }
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/components/atoms/icons/fr-fi-icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,18 @@ export const RecycleIcon: React.FC<IconProps> = ({
title={title}
/>
);

export const UnarchiveIcon: React.FC<IconProps> = ({
color,
large,
small,
title,
}) => (
<FrFiIcon
type="archive-fill"
color={color}
large={large}
small={small}
title={title}
/>
);
9 changes: 9 additions & 0 deletions frontend/src/components/organisms/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type AuthContextType = {
login: () => void;
logout: () => void;
getIsUserAnInstructor: (target_api: string) => boolean;
getIsUserAnAdministrator: () => boolean;
};

export const AuthContext = React.createContext<AuthContextType>({
Expand All @@ -22,6 +23,7 @@ export const AuthContext = React.createContext<AuthContextType>({
login: () => {},
logout: () => {},
getIsUserAnInstructor: (target_api) => false,
getIsUserAnAdministrator: () => false,
});

export const useAuth = () => {
Expand Down Expand Up @@ -110,6 +112,12 @@ export class AuthStore extends React.Component<{ children: React.ReactNode }> {
return this.state.user.roles.includes(targetApiInstructorRole);
};

getIsUserAnAdministrator = () => {
const administratorRole = 'administrator';

return this.state.user.roles.includes(administratorRole);
};

render() {
const { children }: { children?: React.ReactNode } = this.props;
const { user, isLoading, connectionError } = this.state;
Expand All @@ -123,6 +131,7 @@ export class AuthStore extends React.Component<{ children: React.ReactNode }> {
login: this.login,
logout: this.logout,
getIsUserAnInstructor: this.getIsUserAnInstructor,
getIsUserAnAdministrator: this.getIsUserAnAdministrator,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
MailOpenIcon,
InfoFillIcon,
WarningIcon,
UnarchiveIcon,
} from '../../../atoms/icons/fr-fi-icons';
import FileCopyIcon from '../../../atoms/icons/fileCopy';
import { Linkify } from '../../../molecules/Linkify';
Expand Down Expand Up @@ -93,6 +94,10 @@ const eventToDisplayableContent = {
icon: <InfoFillIcon color={'var(--text-default-warning)'} />,
label: 'a répondu à un avis',
},
[EnrollmentEvent.unarchive]: {
icon: <UnarchiveIcon color={'var(--text-default-info)'} />,
label: 'a désarchivé l’habilitation',
},
};

export const EventItem: React.FC<Event> = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@
border-top: 1px solid var(--border-default-grey);
border-bottom: 1px solid var(--border-default-grey);
}

.admin-unarchive-section {
display: flex;
justify-content: space-between;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isEmpty } from 'lodash';
import { useContext } from 'react';
import React, { useContext, useEffect, useRef, useState } from 'react';
import Badge, { BadgeType } from '../../../atoms/hyperTexts/Badge';
import Link from '../../../atoms/hyperTexts/Link';
import { StatusBadge } from '../../../molecules/StatusBadge';
Expand All @@ -10,19 +10,75 @@ import ActivityFeed from './ActivityFeed';
import './index.css';
import NotificationSubSection from './NotificationSubSection';
import { Event } from '../../../../config';
import Button from '../../../atoms/hyperTexts/Button';
import { useAuth } from '../../AuthContext';
import { unarchiveEnrollment } from '../../../../services/enrollments';
import ConfirmationModal from '../../ConfirmationModal';
import Alert, { AlertType } from '../../../atoms/Alert';
import { Linkify } from '../../../molecules/Linkify';

export const HeadSection = () => {
const {
enrollment: { id, target_api, status, copied_from_enrollment_id, events },
} = useContext(FormContext)!;

const { label } = useDataProvider(target_api);
const { getIsUserAnAdministrator } = useAuth();

const isUserAnAdministrator = getIsUserAnAdministrator();
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
const isArchived = status === 'archived';
const [showAlert, setShowAlert] = useState(false);
const alertRef = useRef(null);

const openConfirmationModal = () => {
setIsConfirmationModalOpen(true);
};

const closeConfirmationModal = () => {
setIsConfirmationModalOpen(false);
};

const handleUnarchive = () => {
unarchiveEnrollment({ id: id });
closeConfirmationModal();
setShowAlert(true);
};

useEffect(() => {
// Scroll to the alert when it is displayed
if (showAlert && alertRef.current) {
alertRef.current.scrollIntoView({ behavior: 'smooth' });
}
}, [showAlert]);

return (
<ScrollablePanel scrollableId="head">
<div className="badge-sub-section fr-mb-3w">
<>Vous demandez l’accès à</>
<h1>{label}</h1>
<div className="admin-unarchive-section">
<h1>{label}</h1>
<div>
{!showAlert && isUserAnAdministrator && isArchived && (
<Button
secondary
large
icon="arrow-go-back"
onClick={() => openConfirmationModal()}
>
Désarchiver
</Button>
)}

{showAlert && (
<div ref={alertRef}>
<Alert type={AlertType.success}>
<Linkify message={"L'habilitation a bien été désarchivée"} />
</Alert>
</div>
)}
</div>
</div>
<div className="datapass-badge-group">
{id && <Badge type={BadgeType.info}>Habilitation n°{id}</Badge>}
<StatusBadge status={status} />
Expand All @@ -39,6 +95,20 @@ export const HeadSection = () => {
<div className="fr-pt-3w">
<NotificationSubSection />
</div>

{/* Render the ConfirmationModal */}
{isConfirmationModalOpen && (
<ConfirmationModal
title="Voulez-vous décarchiver l'habilitation ?"
handleConfirm={handleUnarchive}
handleCancel={closeConfirmationModal}
>
<p>
Vous êtes sur le point de désarchiver cette habilitation, elle
reprendra son statut initial dans votre liste d'habilitations.
</p>
</ConfirmationModal>
)}
</ScrollablePanel>
);
};
Expand Down
1 change: 1 addition & 0 deletions frontend/src/config/event-configuration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum EnrollmentEvent {
instruct = 'instruct',
update_contacts = 'update_contacts',
copy = 'copy',
unarchive = 'unarchive',
}

export enum PromptType {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/process-event.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NetworkError, getErrorMessages } from '.';
import { getErrorMessages, NetworkError } from '.';
import {
EnrollmentEvent,
EventConfiguration,
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/services/enrollments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,11 @@ export function markEventAsRead({
)
.then(({ data }) => data);
}

export function unarchiveEnrollment({ id }: { id: number }) {
return httpClient
.patch(`${BACK_HOST}/api/enrollments/${id}/unarchive`, {
headers: { 'Content-type': 'application/json' },
})
.then(({ data }) => data);
}
Loading