diff --git a/DigitalLearningSolutions.Data.Migrations/202501280929_CreateCompetencyAssessmentTables.cs b/DigitalLearningSolutions.Data.Migrations/202501280929_CreateCompetencyAssessmentTables.cs new file mode 100644 index 0000000000..3ad9669e2c --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202501280929_CreateCompetencyAssessmentTables.cs @@ -0,0 +1,57 @@ +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + using FluentMigrator.SqlServer; + [Migration(202501280929)] + public class CreateCompetencyAssessmentTables : Migration + { + public override void Up() + { + Create.Table("SelfAssessmentFrameworks") + .WithColumn("ID").AsInt32().NotNullable().PrimaryKey().Identity() + .WithColumn("SelfAssessmentId").AsInt32().NotNullable().ForeignKey("SelfAssessments", "ID") + .WithColumn("FrameworkId").AsInt32().NotNullable().ForeignKey("Frameworks", "ID") + .WithColumn("CreatedDate").AsDateTime().NotNullable().WithDefault(SystemMethods.CurrentDateTime) + .WithColumn("CreatedByAdminId").AsInt32().NotNullable().ForeignKey("AdminAccounts", "ID") + .WithColumn("RemovedDate").AsDateTime().Nullable() + .WithColumn("RemovedByAdminId").AsInt32().Nullable().ForeignKey("AdminAccounts", "ID") + .WithColumn("AmendedDate").AsDateTime().Nullable() + .WithColumn("AmendedByAdminId").AsInt32().Nullable().ForeignKey("AdminAccounts", "ID"); + Create.Table("SelfAssessmentTaskStatus") + .WithColumn("ID").AsInt32().NotNullable().PrimaryKey().Identity() + .WithColumn("SelfAssessmentId").AsInt32().NotNullable().ForeignKey("SelfAssessments", "ID").Unique() + .WithColumn("IntroductoryTextTaskStatus").AsBoolean().Nullable() + .WithColumn("BrandingTaskStatus").AsBoolean().Nullable() + .WithColumn("VocabularyTaskStatus").AsBoolean().Nullable() + .WithColumn("WorkingGroupTaskStatus").AsBoolean().Nullable() + .WithColumn("NationalRoleProfileTaskStatus").AsBoolean().Nullable() + .WithColumn("FrameworkLinksTaskStatus").AsBoolean().Nullable() + .WithColumn("SelectCompetenciesTaskStatus").AsBoolean().Nullable() + .WithColumn("OptionalCompetenciesTaskStatus").AsBoolean().Nullable() + .WithColumn("RoleRequirementsTaskStatus").AsBoolean().Nullable() + .WithColumn("SupervisorRolesTaskStatus").AsBoolean().Nullable() + .WithColumn("SelfAssessmentOptionsTaskStatus").AsBoolean().Nullable() + .WithColumn("ReviewTaskStatus").AsBoolean().Nullable(); + Alter.Table("SelfAssessments").AlterColumn("Description").AsString(int.MaxValue).Nullable(); + Execute.Sql($@"INSERT INTO SelfAssessmentFrameworks (SelfAssessmentId, FrameworkId, CreatedByAdminId) + SELECT sa.ID, fc.FrameworkID, sa.CreatedByAdminID + FROM SelfAssessments AS sa INNER JOIN + SelfAssessmentStructure AS sas ON sa.ID = sas.SelfAssessmentID INNER JOIN + FrameworkCompetencies AS fc ON sas.CompetencyID = fc.CompetencyID + GROUP BY sa.ID, fc.FrameworkID, sa.CreatedByAdminID + "); + Execute.Sql($@"INSERT INTO SelfAssessmentTaskStatus (SelfAssessmentId, IntroductoryTextTaskStatus, BrandingTaskStatus, VocabularyTaskStatus, WorkingGroupTaskStatus, NationalRoleProfileTaskStatus, FrameworkLinksTaskStatus, SelectCompetenciesTaskStatus, OptionalCompetenciesTaskStatus, RoleRequirementsTaskStatus, SupervisorRolesTaskStatus, SelfAssessmentOptionsTaskStatus) + SELECT ID, 1,1,1,1,1,1,1,1,1,1,1 + FROM SelfAssessments AS sa + "); + } + + public override void Down() + { + Delete.Table("SelfAssessmentFrameworks"); + Delete.Table("SelfAssessmentTaskStatus"); + Alter.Table("SelfAssessments").AlterColumn("Description").AsString(int.MaxValue).NotNullable(); + } + + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202504241517_AddFieldIsPrimaryToSelfAssessmentFrameworksTable.cs b/DigitalLearningSolutions.Data.Migrations/202504241517_AddFieldIsPrimaryToSelfAssessmentFrameworksTable.cs new file mode 100644 index 0000000000..fe1e096135 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202504241517_AddFieldIsPrimaryToSelfAssessmentFrameworksTable.cs @@ -0,0 +1,12 @@ +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + [Migration(202504241517)] + public class AddFieldIsPrimaryToSelfAssessmentFrameworksTable : AutoReversingMigration + { + public override void Up() + { + Alter.Table("SelfAssessmentFrameworks").AddColumn("IsPrimary").AsBoolean().NotNullable().WithDefaultValue(1); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202507111455_UpdatePrivacyNoticeAcceptableUse.cs b/DigitalLearningSolutions.Data.Migrations/202507111455_UpdatePrivacyNoticeAcceptableUse.cs new file mode 100644 index 0000000000..b9dc84ab7e --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202507111455_UpdatePrivacyNoticeAcceptableUse.cs @@ -0,0 +1,430 @@ +using FluentMigrator; +namespace DigitalLearningSolutions.Data.Migrations +{ + [Migration(202507111455)] + public class UpdatePrivacyNoticeAcceptableUse : Migration + { + public override void Up() + { + var PrivacyPolicyNew = @"

PRIVACY NOTICE

This page explains our privacy policy and how we will use and protect any information about you that you give to us or that we collate when you visit this website, or undertake employment with NHS England (NHSE or we/us/our), or participate in any NHSE sponsored training, education and development including via any of our training platform websites (Training).

This privacy notice is intended to provide transparency regarding what personal data NHSE may hold about you, how it will be processed and stored, how long it will be retained and who may have access to your data.

Personal data is any information relating to an identified or identifiable living person (known as the data subject). An identifiable person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number or factors specific to the physical, genetic or mental identity of that person, for example.

1 OUR ROLE IN THE NHS

We are here to improve the quality of healthcare for the people and patients of England through education, training and lifelong development of staff and appropriate planning of the workforce required to deliver healthcare services in England.

We aim to enable high quality, effective, compassionate care and to identify the right people with the right skills and the right values. All the information we collect is to support these objectives.

2 IMPORTANT INFORMATION

NHSE is the data controller in respect of any personal data it holds concerning trainees in Training, individuals employed by NHSE and individuals accessing NHSE’s website.

We have appointed a data protection officer (DPO) who is responsible for overseeing questions in relation to this privacy policy. If you have any questions about this privacy policy or our privacy practices, want to know more about how your information will be used, or make a request to exercise your legal rights, please contact our DPO in the following ways:

Name: Andrew Todd

Email address: gdpr@hee.nhs.uk

Postal address: NHS England of Skipton House, 80 London Road, London SE1 6LH

3 WHAT THIS PRIVACY STATEMENT COVERS

This privacy statement only covers the processing of personal data by NHSE that NHSE has obtained from data subjects accessing any of NHSE’s websites and from its provision of services and exercise of functions. It does not cover the processing of data by any sites that can be linked to or from NHSE’s websites, so you should always be aware when you are moving to another site and read the privacy statement on that website.

When providing NHSE with any of your personal data for the first time, for example, when you take up an appointment with NHSE or when you register for any Training, you will be asked to confirm that you have read and accepted the terms of this privacy statement. A copy of your acknowledgement will be retained for reference.

If our privacy policy changes in any way, we will place an updated version on this page. Regularly reviewing the page ensures you are always aware of what information we collect, how we use it and under what circumstances, if any, we will share it with other parties.

4 WHY NHSE COLLECTS YOUR PERSONAL DATA

Personal data may be collected from you via the recruitment process, when you register and/or create an account for any Training, during your Annual Review of Competence Progression or via NHSE’s appraisal process. Personal data may also be obtained from Local Education Providers or employing organisations in connection with the functions of NHSE.

Your personal data is collected and processed for the purposes of and in connection with the functions that NHSE performs with regard to Training and planning. The collection and processing of such data is necessary for the purposes of those functions.

A full copy of our notification to the Information Commissioner’s Office (ICO) (the UK regulator for data protection issues), can be found on the ICO website here: www.ico.org.uk by searching NHSE’s ICO registration number, which is Z2950066.

In connection with Training, NHSE collects and uses your personal information for the following purposes:

  1. to manage your Training and programme, including allowing you to access your own learning history;
  2. to quality assure Training programmes and ensure that standards are maintained, including gathering feedback or input on the service, content, or layout of the Training and customising the content and/or layout of the Training;
  3. to identify workforce planning targets;
  4. to maintain patient safety through the management of performance concerns;
  5. to comply with legal and regulatory responsibilities including revalidation;
  6. to contact you about Training updates, opportunities, events, surveys and information that may be of interest to you;
  7. transferring your Training activity records for programmes to other organisations involved in medical training in the healthcare sector. These organisations include professional bodies that you may be a member of, such as a medical royal college or foundation school; or employing organisations, such as trusts;
  8. making your Training activity records visible to specific named individuals, such as tutors, to allow tutors to view their trainees’ activity. We would seek your explicit consent before authorising anyone else to view your records;
  9. providing anonymous, summarised data to partner organisations, such as professional bodies; or local organisations, such as strategic health authorities or trusts;
  10. for NHSE internal review;
  11. to provide HR related support services and Training to you, for clinical professional learner recruitment;
  12. to promote our services;
  13. to monitor our own accounts and records;
  14. to monitor our work, to report on progress made; and
  15. to let us fulfil our statutory obligations and statutory returns as set by the Department of Health and the law (for example complying with NHSE’s legal obligations and regulatory responsibilities under employment law).

Further information about our use of your personal data in connection with Training can be found in ’A Reference Guide for Postgraduate Foundation and Specialty Training in the UK’, published by the Conference of Postgraduate Medical Deans of the United Kingdom and known as the ‘Gold Guide’, which can be found here: https://www.copmed.org.uk/gold-guide.

5 TYPES OF PERSONAL DATA COLLECTED BY NHSE

The personal data that NHSE collects when you register for Training enables the creation of an accurate user profile/account, which is necessary for reporting purposes and to offer Training that is relevant to your needs.

The personal data that is stored by NHSE is limited to information relating to your work, such as your job role, place of work, and membership number for a professional body (e.g. your General Medical Council number). NHSE will never ask for your home address or any other domestic information.

When accessing Training, you will be asked to set up some security questions, which may contain personal information. These questions enable you to log in if you forget your password and will never be used for any other purpose. The answers that you submit when setting up these security questions are encrypted in the database so no one can view what has been entered, not even NHSE administrators.

NHSE also store a record of some Training activity, including upload and download of Training content, posts on forums or other communication media, and all enquires to the service desks that support the Training.

If you do not provide personal data that we need from you when requested, we may not be able to provide services (such as Training) to you. In this case, we may have to cancel such service, but we will notify you at the time if this happens.

6 COOKIES

When you access NHSE’s website and Training, we want to make them easy, useful and reliable. This sometimes involves placing small amounts of limited information on your device (such as your computer or mobile phone). These small files are known as cookies, and we ask you to agree to their usage in accordance with ICO guidance.

These cookies are used to improve the services (including the Training) we provide you through, for example:

  1. enabling a service to recognise your device, so you do not have to give the same information several times during one task (e.g. we use a cookie to remember your username if you check the ’Remember Me’ box on a log in page);
  2. recognising that you may already have given a username and password, so you do not need to do it for every web page requested;
  3. measuring how many people are using services, so they can be made easier to use and there is enough capacity to ensure they are fast; and
  4. analysing anonymised data to help us understand how people interact with services so we can make them better.

We use a series of cookies to monitor website speed and usage, as well as to ensure that any preferences you have selected previously are the same when you return to our website. Please visit our cookie policies page to understand the cookies that we use: https://www.dls.nhs.uk/v2/CookieConsent/CookiePolicy

Most cookies applied when accessing Training are used to keep track of your input when filling in online forms, known as session-ID cookies, which are exempt from needing consent as they are deemed essential for using the website or Training they apply to. Some cookies, like those used to measure how you use the Training, are not needed for our website to work. These cookies can help us improve the Training, but we’ll only use them if you say it’s OK. We’ll use a cookie to save your settings.

On a number of pages on our website or Training, we use ’plug-ins’ or embedded media. For example, we might embed YouTube videos. Where we have used this type of content, the suppliers of these services may also set cookies on your device when you visit their pages. These are known as ’third-party’ cookies. To opt-out of third-parties collecting any data regarding your interaction on our website, please refer to their websites for further information.

We will not use cookies to collect personal data about you. However, if you wish to restrict or block the cookies which are set by our websites or Training, or indeed any other website, you can do this through your browser settings. The ’Help’ function within your browser should tell you how. Alternatively, you may wish to visit www.aboutcookies.org which contains comprehensive information on how to do this on a wide variety of browsers. You will also find details on how to delete cookies from your machine as well as more general information about cookies. Please be aware that restricting cookies may impact on the functionality of our website.

7 LEGAL BASIS FOR PROCESSING

The retained EU law version of the General Data Protection Regulation ((EU) 2016/679) (UK GDPR) requires that data controllers and organisations that process personal data demonstrate compliance with its provisions. This involves publishing our basis for lawful processing of personal data.

As personal data is processed for the purposes of NHSE’s statutory functions, NHSE’s legal bases for the processing of personal data as listed in Article 6 of the UK GDPR are as follows:

Where NHSE processes special categories of personal data, its additional legal bases for processing such data as listed in Article 9 of the UK GDPR are as follows:

Special categories of personal data include data relating to racial or ethnic origin, political opinions, religious beliefs, sexual orientation and data concerning health.

Please note that not all of the above legal bases will apply for each type of processing activity that NHSE may undertake. However, when processing any personal data for any particular purpose, one or more of the above legal bases will apply.

We may seek your consent for some processing activities, for example for sending out invitations to you to Training events and sending out material from other government agencies. If you do not give consent for us to use your data for these purposes, we will not use your data for these purposes, but your data may still be retained by us and used by us for other processing activities based on the above lawful conditions for processing set out above.

8 INFORMATION THAT WE MAY NEED TO SEND YOU

We may occasionally have to send you information from NHSE, the Department of Health, other public authorities and government agencies about matters of policy where those policy issues impact on Training, workforce planning, or other matters related to NHSE. This is because NHSE is required by statute to exercise functions of the Secretary of State in respect of Training and workforce planning. If you prefer, you can opt out of receiving information about general matters of policy impacting on Training and workforce planning by contacting your Local Office recruitment lead or tel@hee.nhs.uk. The relevant Local Office or a representative from the relevant training platform website will provide you with further advice and guidance regarding any consequences of your request.

NHSE will not send you generic information from other public authorities and government agencies on issues of government policy.

9 TRANSFERS ABROAD

The UK GDPR imposes restrictions on the transfer of personal data outside the European Union, to third countries or international organisations, in order to ensure that the level of protection of individuals afforded by the UK GDPR is not undermined.

Your data may only be transferred abroad where NHSE is assured that a third country, a territory or one or more specific sectors in the third country, or an international organisation ensures an adequate level of protection.

10 HOW WE PROTECT YOUR PERSONAL DATA

Our processing of all personal data complies with the UK GDPR principles. We have put in place appropriate security measures to prevent your personal data from being accidentally lost, used or accessed in an unauthorised way, altered or disclosed. The security of the data is assured through the implementation of NHSE’s policies on information governance management.

The personal data we hold may be held as an electronic record on data systems managed by NHSE, or as a paper record. These records are only accessed, seen and used in the following circumstances:

  1. if required and/or permitted by law; or
  2. by NHSE staff who need access to them so they can do their jobs and who are subject to a duty of confidentiality; or
  3. by other partner organisations, including our suppliers, who have been asked to sign appropriate non-disclosure or data sharing agreements and will never be allowed to use the information for commercial purposes.

We make every effort to keep your personal information accurate and up to date, but in some cases we are reliant on you as the data subject to notify us of any necessary changes to your personal data. If you tell us of any changes in your circumstances, we can update the records with personal data you choose to share with us.

Information collected by NHSE will never be sold for financial gain or shared with other organisations for commercial purposes.

We have put in place procedures to deal with any suspected personal data breach and will notify you and any applicable regulator of a breach where we are legally required to do so.

11 SHARING PERSONAL DATA

So we can provide the right services at the right level, we may share your personal data within services across NHSE and with other third party organisations such as the Department of Health, higher education institutions, clinical placement providers, colleges, faculties, other NHSE Local Offices, the General Medical Council, NHS Trusts/Health Boards/Health and Social Care Trusts, approved academic researchers and other NHS and government agencies where necessary, to provide the best possible Training and to ensure that we discharge NHSEs responsibilities for employment and workforce planning for the NHS. This will be on a legitimate need to know basis only.

We may also share information, where necessary, to prevent, detect or assist in the investigation of fraud or criminal activity, to assist in the administration of justice, for the purposes of seeking legal advice or exercising or defending legal rights or as otherwise required by the law.

Where the data is used for analysis and publication by a recipient or third party, any publication will be on an anonymous basis, and will not make it possible to identify any individual. This will mean that the data ceases to become personal data.

12 HOW LONG WE RETAIN YOUR PERSONAL DATA

We will keep personal data for no longer than necessary to fulfil the purposes we collected it for, in accordance with our records management policy and the NHS records retention schedule within the NHS Records Management Code of Practice at: https://www.england.nhs.uk/contact-us/privacy-notice/nhs-england-as-a-data-controller (as may be amended from time to time).

In some circumstances you can ask us to delete your data. Please see the “Your rights” section below for further information.

In some circumstances we will anonymise your personal data (so that it can no longer be associated with you) for research or statistical purposes, in which case we may use this information indefinitely without further notice to you.

13 OPEN DATA

Open data is data that is published by central government, local authorities and public bodies to help you build products and services. NHSE policy is to observe the Cabinet Office transparency and accountability commitments towards more open use of public data in accordance with relevant and applicable UK legislation.

NHSE would never share personal data through the open data facility. To this end, NHSE will implement information governance protocols that reflect the ICO’s recommended best practice for record anonymisation, and Office of National Statistics guidance on publication of statistical information.

14 YOUR RIGHTS

14.1 Right to rectification and erasure

Under the UK GDPR you have the right to rectification of inaccurate personal data and the right to request the erasure of your personal data. However, the right to erasure is not an absolute right and it may be that it is necessary for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.

14.2 Right to object and withdraw your consent

You have the right in certain circumstances to ask NHSE to stop processing your personal data in relation to any NHSE service. As set out above, you can decide that you do not wish to receive information from NHSE about matters of policy affecting Training and workforce. However, the right to object is not an absolute right and it may be that it is necessary in certain circumstances for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.

If you object to the way in which NHSE is processing your personal information or if you wish to ask NHSE to stop processing your personal data, please contact your relevant Local Office.

Please note, if we do stop processing personal data about you, this may prevent NHSE from providing the best possible service to you. Withdrawing your consent will result in your Training account being anonymised and access to the Training removed.

14.3 Right to request access

You can access a copy of the information NHSE holds about you by writing to NHSE’s Public and Parliamentary Accountability Team. This information is generally available to you free of charge subject to the receipt of appropriate identification. More information about subject access requests can be found here: https://www.hee.nhs.uk/about/contact-us/subject-access-request.

14.4 Right to request a transfer

The UK GDPR sets out the right for a data subject to have their personal data ported from one controller to another on request in certain circumstances. You should discuss any request for this with your Local Office. This right only applies to automated information which you initially provided consent for us to use or where we used the information to perform a contract with you.

14.5 Right to restrict processing

You can ask us to suspend the processing of your personal data if you want us to establish the data’s accuracy, where our use of the data is unlawful but you do not want us to erase it, where you need us to hold the data even if we no longer require it as you need it to establish, exercise or defend legal claims or where you have objected to our use of your data but we need to verify whether we have overriding legitimate grounds to use it.

14.6 Complaints

You have the right to make a complaint at any time to the ICO. We would, however, appreciate the chance to deal with your concerns before you approach the ICO so please contact your Local Office or the DPO in the first instance, using the contact details above.

You can contact the ICO at the following address:

The Office of the Information Commissioner
Wycliffe House
Water Lane
Wilmslow
Cheshire
SK9 5AF

14.7 Your responsibilities

It is important that you work with us to ensure that the information we hold about you is accurate and up to date so please inform NHSE if any of your personal data needs to be updated or corrected.

All communications from NHSE will normally be by email. It is therefore essential for you to maintain an effective and secure email address, or you may not receive information or other important news and information about your employment or Training.

"; + + Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + PrivacyPolicyNew + "'" + + "where ConfigName='PrivacyPolicy' AND IsHtml = 1"); + + var AcceptableUseNew = @"

ACCEPTABLE USE POLICY

+
    +
  1. + General +
      +
    1. This Acceptable Use Policy sets out how we permit you to use any of our Platforms. Your compliance with this Acceptable Use Policy is a condition of your use of the Platform.
    2. +
    3. Capitalised terms have the meaning given to them in the terms of use for the Platform which are available at https://www.dls.nhs.uk/v2/LearningSolutions/Terms.
    4. +
    +
  2. +
  3. + Acceptable use +
      +
    1. You are permitted to use the Platform as set out in the Terms and for the purpose of personal study.
    2. +
    3. You must not use any part of the Content on the Platform for commercial purposes without obtaining a licence to do so from us or our licensors.
    4. +
    5. If you print off, copy, download, share or repost any part of the Platform in breach of this Acceptable Use Policy, your right to use the Platform will cease immediately and you must, at our option, return or destroy any copies of the materials you have made.
    6. +
    7. Our status (and that of any identified contributors) as the authors of Content on the Platform must always be acknowledged (except in respect of Third-Party Content).
    8. +
    +
  4. +
  5. + Prohibited uses +
      +
    1. + You may not use the Platform: +
        +
      1. in any way that breaches any applicable local, national or international law or regulation;
      2. +
      3. in any way that is unlawful or fraudulent or has any unlawful or fraudulent purpose or effect;
      4. +
      5. in any way that infringes the rights of, or restricts or inhibits the use and enjoyment of this site by any third party;
      6. +
      7. for the purpose of harming or attempting to harm minors in any way;
      8. +
      9. to bully, insult, intimidate or humiliate any person;
      10. +
      11. to send, knowingly receive, upload, download, use or re-use any material which does not comply with our Content Standards as set out in paragraph 4;
      12. +
      13. to transmit, or procure the sending of, any unsolicited or unauthorised advertising or promotional material or any other form of similar solicitation (spam), or any unwanted or repetitive content that may cause disruption to the Platform or diminish the user experience, of the Platform’s usefulness or relevant to others;
      14. +
      15. to do any act or thing with the intention of disrupting the Platform in any way, including uploading any malware or links to malware, or introduce any virus, trojan, worm, logic bomb or other material that is malicious or technologically harmful or other potentially damaging items into the Platform;
      16. +
      17. to knowingly transmit any data, send or upload any material that contains viruses, Trojan horses, worms, time-bombs, keystroke loggers, spyware, adware or any other harmful programs or similar computer code designed to adversely affect the operation of any computer software or hardware; or
      18. +
      19. to upload terrorist content.
      20. +
      +
    2. +
    3. + You also agree: +
        +
      1. to follow any reasonable instructions given to you by us in connection with your use of the Platform;
      2. +
      3. to respect the rights and dignity of others, in order to maintain the ethos and good reputation of the NHS, the public good generally and the spirit of cooperation between those studying and working within the health and care sector. In particular, you must act in a professional manner with regard to all other users of the Platform at all times;
      4. +
      5. + not to modify or attempt to modify any of the Content, save: +
          +
        1. in respect of Contributions;
        2. +
        3. where you are the editor of a catalogue within the Learning Hub, you may alter Content within that catalogue;
        4. +
        +
      6. +
      7. not to download or copy any of the Content to electronic or photographic media;
      8. +
      9. not to reproduce any part of the Content by any means or under any format other than as a reasonable aid to your personal study;
      10. +
      11. not to reproduce, duplicate, copy or re-sell any Content in contravention of the provisions of this Acceptable Use Policy; and
      12. +
      13. not to use tools that automatically perform actions on your behalf;
      14. +
      15. not to upload any content that infringes the intellectual property rights, privacy rights or any other rights of any person or organisation; and
      16. +
      17. not to attempt to disguise your identity or that of your organisation;
      18. +
      19. + not to access without authority, interfere with, damage or disrupt: +
          +
        1. any part of the Platform;
        2. +
        3. any equipment or network on which the Platform is stored;
        4. +
        5. any software used in the provision of the Platform;
        6. +
        7. the server on which the Platform is stored;
        8. +
        9. any computer or database connected to the Platform; or
        10. +
        11. any equipment or network or software owned or used by any third party.
        12. +
        +
      20. +
      21. not to attack the Platform via a denial-of-service attack or a distributed denial-of-service attack.
      22. +
      +
    4. +
    +
  6. +
  7. + Content standards +
      +
    1. The content standards set out in this paragraph 4 (Content Standards) apply to any and all Contributions.
    2. +
    3. The Content Standards must be complied with in spirit as well as to the letter. The Content Standards apply to each part of any Contribution as well as to its whole.
    4. +
    5. We will determine, in our discretion, whether a Contribution breaches the Content Standards.
    6. +
    7. + A Contribution must: +
        +
      1. be accurate (where it states facts);
      2. +
      3. be genuinely held (where it states opinions); and
      4. +
      5. comply with the law applicable in England and Wales and in any country from which it is posted.
      6. +
      +
    8. +
    9. + A Contribution must not: +
        +
      1. + contain misinformation that is likely to harm users, patients/service users, health and care workers, or the general public’s wellbeing, safety, trust and reputation, including the reputation of the NHS or any part of it. This could include false and misleading information relating to disease prevention and treatment, conspiracy theories, content that encourages discrimination, harassment or physical violence, content originating from misinformation campaigns, and content edited or manipulated in such a way as to constitute misinformation; +
      2. +
      3. + contain any content or link to any content: +
          +
        1. which is created for advertising, promotional or other commercial purposes, including links, logos and business names;
        2. +
        3. which requires a subscription or payment to gain access to such content;
        4. +
        5. in which the user has a commercial interest;
        6. +
        7. which promotes a business name and/or logo;
        8. +
        9. which contains a link to an app via iOS or Google Play; or
        10. +
        11. which has as its purpose or effect the collection and sharing of personal data;
        12. +
        +
      4. +
      5. + be irrelevant to the purpose or aims of the Platform or while addressing relevant subject matter, contain an irrelevant, unsuitable or inappropriate slant (for example relating to potentially controversial opinions or beliefs of any kind intended to influence others); +
      6. +
      7. be defamatory of any person;
      8. +
      9. be obscene, offensive, hateful or inflammatory, or contain any profanity;
      10. +
      11. bully, insult, intimidate or humiliate;
      12. +
      13. + encourage suicide, substance abuse, eating disorders or other acts of self-harm.Content related to self - harm for the purposes of therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally; +
      14. +
      15. + feature sexual imagery purely intended to stimulate sexual arousal. Non - pornographic content relating to sexual health and related issues, surgical procedures and the results of surgical procedures, breastfeeding, therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally; +
      16. +
      17. + include child sexual abuse material. Content relating to safeguarding which addresses the subject of child sexual abuse may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally; +
      18. +
      19. + incite or glorify violence including content designed principally for the purposes of causing reactions of shock or disgust; +
      20. +
      21. + promote discrimination or discriminate in respect of the protected characteristics set out in the Equality Act 2010, being age, disability, gender reassignment, marriage and civil partnership, pregnancy and maternity, race, nationality, religion or belief, sex, and sexual orientation; +
      22. +
      23. infringe any copyright, database right or trade mark of any other person;
      24. +
      25. be likely to deceive any person;
      26. +
      27. breach any legal duty owed to a third party, such as a contractual duty or a duty of confidence;
      28. +
      29. + promote any illegal content or activity, including but not limited to the encouragement, promotion, justification, praise or provision of aid to dangerous persons or organisations, including extremists, terrorists and terrorist organisations and those engaged in any form of criminal activity; +
      30. +
      31. be in contempt of court;
      32. +
      33. + be threatening, abuse or invade another''s privacy, or cause annoyance, inconvenience or needless anxiety; +
      34. +
      35. be likely to harass, bully, shame, degrade, upset, embarrass, alarm or annoy any other person;
      36. +
      37. impersonate any person or misrepresent your identity or affiliation with any person;
      38. +
      39. + advocate, promote, incite any party to commit, or assist any unlawful or criminal act such as (by way of example only) copyright infringement or computer misuse; +
      40. +
      41. + contain a statement which you know or believe, or have reasonable grounds for believing, that members of the public to whom the statement is, or is to be, published are likely to understand as a direct or indirect encouragement or other inducement to the commission, preparation or instigation of acts of terrorism; +
      42. +
      43. contain harmful material;
      44. +
      45. give the impression that the Contribution emanates from us, if this is not the case; or
      46. +
      47. disclose any third party’s confidential information, identity, personally identifiable information or personal data (including data concerning health).
      48. +
      +
    10. +
    11. You acknowledge and accept that, when using the Platform and accessing the Content, some Content that has been uploaded by third parties may be factually inaccurate, or the topics addressed by the Content may be offensive, indecent, or objectionable in nature. We are not responsible (legally or otherwise) for any claim you may have in relation to the Content.
    12. +
    13. When producing Content to upload to the Platform, we encourage you to implement NICE guideline recommendations and ensure that sources of evidence are valid (for example, by peer review).
    14. +
    +
  8. +
  9. + Metadata +

    When making any Contribution, you must where prompted include a sufficient description of the Content so that other users can understand the description, source, and age of the Content. For example, if Content has been quality assured, then the relevant information should be posted in the appropriate field. All metadata fields on the Platform must be completed appropriately before initiating upload. Including the correct information is important in order to help other users locate the Content (otherwise the Content may not appear in search results for others to select).

    +
  10. +
  11. + Updates +

    You must update each Contribution at least once every 3 (three) years, or update or remove it should it cease to be relevant or become outdated or revealed or generally perceived to be unsafe or otherwise unsuitable for inclusion on the Platform.

    +
  12. +
  13. + Accessibility +

    Where practicable, all Contributions should aim to meet the accessibility standards as described in our Accessibility Statement + [https://www.dls.nhs.uk/v2/LearningSolutions/AccessibilityHelp] and as set out in the AA Standard Web Content Accessibility Guidelines v2.1 found here: + https://www.w3.org/TR/WCAG21/.

    +
  14. +
  15. + Rules about linking to the Platform +
      +
    1. The Platform must not be framed on any other site.
    2. +
    3. You may directly link to any Content that is hosted on the Platform, however, please be aware that not all links will continue to be available indefinitely. We will use our best efforts to ensure that all links are valid at the time of creating the related Content but cannot be held responsible for any subsequent changes to the link address or related Content.
    4. +
    +
  16. +
  17. + No text or data mining, or web scraping +
      +
    1. + You shall not conduct, facilitate, authorize or permit any text or data mining or web scraping in relation to the Platform or any services provided via, or in relation to, the Platform. This includes using (or permitting, authorizing or attempting the use of): +
        +
      1. any ""robot"", ""bot"", ""spider"", ""scraper"" or other automated device, program, tool, algorithm, code, process or methodology to access, obtain, copy, monitor or republish any portion of the Platform or any data, Content, information or services accessed via the same; and/or
      2. +
      3. any automated analytical technique aimed at analyzing text and data in digital form to generate information which includes but is not limited to patterns, trends, and correlations.
      4. +
      +
    2. +
    3. The provisions in this paragraph should be treated as an express reservation of our rights in this regard, including for the purposes of Article 4(3) of Digital Copyright Directive ((EU) 2019/790).
    4. +
    5. This paragraph shall not apply insofar as (but only to the extent that) we are unable to exclude or limit text or data mining or web scraping activity by contract under the laws which are applicable to us.
    6. +
    +
  18. +
  19. + Breach of this Acceptable Use Policy +

    Failure to comply with this Acceptable Use Policy constitutes a material breach of this Acceptable Use Policy upon which you are permitted to use the Platform and may result in our taking all or any of the following actions:

    +
      +
    1. Immediate, temporary, or permanent withdrawal of your right to use the Platform;
    2. +
    3. Immediate, temporary, or permanent removal of any Contribution uploaded by you to the Platform;
    4. +
    5. Issue of a warning to you;
    6. +
    7. Legal proceedings against you for reimbursement of all costs...
    8. +
    9. Disclosure of such information to law enforcement authorities...
    10. +
    11. Any other action we reasonably deem appropriate.
    12. +
    +
  20. +
"; + + Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + AcceptableUseNew + "'" + + "where ConfigName='AcceptableUse' AND IsHtml = 1"); + } + public override void Down() + { + var PrivacyPolicyOld = @"

PRIVACY NOTICE

    This page explains our privacy policy and how we will use and protect any information about you that you give to us or that we collate when you visit this website, or undertake employment with NHS England (NHSE or we/us/our), or participate in any NHSE sponsored training, education and development including via any of our training platform websites (Training).
    This privacy notice is intended to provide transparency regarding what personal data NHSE may hold about you, how it will be processed and stored, how long it will be retained and who may have access to your data.
    Personal data is any information relating to an identified or identifiable living person (known as the data subject). An identifiable person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number or factors specific to the physical, genetic or mental identity of that person, for example.

1 OUR ROLE IN THE NHS

    We are here to improve the quality of healthcare for the people and patients of England through education, training and lifelong development of staff and appropriate planning of the workforce required to deliver healthcare services in England.
    We aim to enable high quality, effective, compassionate care and to identify the right people with the right skills and the right values. All the information we collect is to support these objectives.

2 IMPORTANT INFORMATION

    NHSE is the data controller in respect of any personal data it holds concerning trainees in Training, individuals employed by NHSE and individuals accessing NHSE’s website.
    We have appointed a data protection officer (DPO) who is responsible for overseeing questions in relation to this privacy policy. If you have any questions about this privacy policy or our privacy practices, want to know more about how your information will be used, or make a request to exercise your legal rights, please contact our DPO in the following ways:
    Name: Andrew Todd
    Email address: gdpr@hee.nhs.uk
    Postal address: NHS England of Skipton House, 80 London Road, London SE1 6LH

3 WHAT THIS PRIVACY STATEMENT COVERS

    This privacy statement only covers the processing of personal data by NHSE that NHSE has obtained from data subjects accessing any of NHSE’s websites and from its provision of services and exercise of functions. It does not cover the processing of data by any sites that can be linked to or from NHSE’s websites, so you should always be aware when you are moving to another site and read the privacy statement on that website.
    When providing NHSE with any of your personal data for the first time, for example, when you take up an appointment with NHSE or when you register for any Training, you will be asked to confirm that you have read and accepted the terms of this privacy statement. A copy of your acknowledgement will be retained for reference.
    If our privacy policy changes in any way, we will place an updated version on this page. Regularly reviewing the page ensures you are always aware of what information we collect, how we use it and under what circumstances, if any, we will share it with other parties.

4 WHY NHSE COLLECTS YOUR PERSONAL DATA

    Personal data may be collected from you via the recruitment process, when you register and/or create an account for any Training, during your Annual Review of Competence Progression or via NHSE’s appraisal process. Personal data may also be obtained from Local Education Providers or employing organisations in connection with the functions of NHSE.
    Your personal data is collected and processed for the purposes of and in connection with the functions that NHSE performs with regard to Training and planning. The collection and processing of such data is necessary for the purposes of those functions.
    A full copy of our notification to the Information Commissioner’s Office (ICO) (the UK regulator for data protection issues), can be found on the ICO website here: www.ico.org.uk by searching NHSE’s ICO registration number, which is Z2950066.
    In connection with Training, NHSE collects and uses your personal information for the following purposes:
  1. to manage your Training and programme, including allowing you to access your own learning history;
  2. to quality assure Training programmes and ensure that standards are maintained, including gathering feedback or input on the service, content, or layout of the Training and customising the content and/or layout of the Training;
  3. to identify workforce planning targets;
  4. to maintain patient safety through the management of performance concerns;
  5. to comply with legal and regulatory responsibilities including revalidation;
  6. to contact you about Training updates, opportunities, events, surveys and information that may be of interest to you;
  7. transferring your Training activity records for programmes to other organisations involved in medical training in the healthcare sector. These organisations include professional bodies that you may be a member of, such as a medical royal college or foundation school; or employing organisations, such as trusts;
  8. making your Training activity records visible to specific named individuals, such as tutors, to allow tutors to view their trainees’ activity. We would seek your explicit consent before authorising anyone else to view your records;
  9. providing anonymous, summarised data to partner organisations, such as professional bodies; or local organisations, such as strategic health authorities or trusts;
  10. for NHSE internal review;
  11. to provide HR related support services and Training to you, for clinical professional learner recruitment;
  12. to promote our services;
  13. to monitor our own accounts and records;
  14. to monitor our work, to report on progress made; and
  15. to let us fulfil our statutory obligations and statutory returns as set by the Department of Health and the law (for example complying with NHSE’s legal obligations and regulatory responsibilities under employment law).
    Further information about our use of your personal data in connection with Training can be found in ’A Reference Guide for Postgraduate Foundation and Specialty Training in the UK’, published by the Conference of Postgraduate Medical Deans of the United Kingdom and known as the ‘Gold Guide’, which can be found here: https://www.copmed.org.uk/gold-guide.

5 TYPES OF PERSONAL DATA COLLECTED BY NHSE

    The personal data that NHSE collects when you register for Training enables the creation of an accurate user profile/account, which is necessary for reporting purposes and to offer Training that is relevant to your needs.
    The personal data that is stored by NHSE is limited to information relating to your work, such as your job role, place of work, and membership number for a professional body (e.g. your General Medical Council number). NHSE will never ask for your home address or any other domestic information.
    When accessing Training, you will be asked to set up some security questions, which may contain personal information. These questions enable you to log in if you forget your password and will never be used for any other purpose. The answers that you submit when setting up these security questions are encrypted in the database so no one can view what has been entered, not even NHSE administrators.
    NHSE also store a record of some Training activity, including upload and download of Training content, posts on forums or other communication media, and all enquires to the service desks that support the Training.
    If you do not provide personal data that we need from you when requested, we may not be able to provide services (such as Training) to you. In this case, we may have to cancel such service, but we will notify you at the time if this happens.

6 COOKIES

    When you access NHSE’s website and Training, we want to make them easy, useful and reliable. This sometimes involves placing small amounts of limited information on your device (such as your computer or mobile phone). These small files are known as cookies, and we ask you to agree to their usage in accordance with ICO guidance.
    These cookies are used to improve the services (including the Training) we provide you through, for example:
  1. enabling a service to recognise your device, so you do not have to give the same information several times during one task (e.g. we use a cookie to remember your username if you check the ’Remember Me’ box on a log in page);
  2. recognising that you may already have given a username and password, so you do not need to do it for every web page requested;
  3. measuring how many people are using services, so they can be made easier to use and there is enough capacity to ensure they are fast; and
  4. analysing anonymised data to help us understand how people interact with services so we can make them better.
    We use a series of cookies to monitor website speed and usage, as well as to ensure that any preferences you have selected previously are the same when you return to our website. Please visit our cookie policies page to understand the cookies that we use: https://www.dls.nhs.uk/v2/CookieConsent/CookiePolicy
    Most cookies applied when accessing Training are used to keep track of your input when filling in online forms, known as session-ID cookies, which are exempt from needing consent as they are deemed essential for using the website or Training they apply to. Some cookies, like those used to measure how you use the Training, are not needed for our website to work. These cookies can help us improve the Training, but we’ll only use them if you say it’s OK. We’ll use a cookie to save your settings.
    On a number of pages on our website or Training, we use ’plug-ins’ or embedded media. For example, we might embed YouTube videos. Where we have used this type of content, the suppliers of these services may also set cookies on your device when you visit their pages. These are known as ’third-party’ cookies. To opt-out of third-parties collecting any data regarding your interaction on our website, please refer to their websites for further information.
    We will not use cookies to collect personal data about you. However, if you wish to restrict or block the cookies which are set by our websites or Training, or indeed any other website, you can do this through your browser settings. The ’Help’ function within your browser should tell you how. Alternatively, you may wish to visit www.aboutcookies.org which contains comprehensive information on how to do this on a wide variety of browsers. You will also find details on how to delete cookies from your machine as well as more general information about cookies. Please be aware that restricting cookies may impact on the functionality of our website.

7 LEGAL BASIS FOR PROCESSING

    The retained EU law version of the General Data Protection Regulation ((EU) 2016/679) (UK GDPR) requires that data controllers and organisations that process personal data demonstrate compliance with its provisions. This involves publishing our basis for lawful processing of personal data.
    As personal data is processed for the purposes of NHSE’s statutory functions, NHSE’s legal bases for the processing of personal data as listed in Article 6 of the UK GDPR are as follows:
    Where NHSE processes special categories of personal data, its additional legal bases for processing such data as listed in Article 9 of the UK GDPR are as follows:
    Special categories of personal data include data relating to racial or ethnic origin, political opinions, religious beliefs, sexual orientation and data concerning health.
    Please note that not all of the above legal bases will apply for each type of processing activity that NHSE may undertake. However, when processing any personal data for any particular purpose, one or more of the above legal bases will apply.
    We may seek your consent for some processing activities, for example for sending out invitations to you to Training events and sending out material from other government agencies. If you do not give consent for us to use your data for these purposes, we will not use your data for these purposes, but your data may still be retained by us and used by us for other processing activities based on the above lawful conditions for processing set out above.

8 INFORMATION THAT WE MAY NEED TO SEND YOU

    We may occasionally have to send you information from NHSE, the Department of Health, other public authorities and government agencies about matters of policy where those policy issues impact on Training, workforce planning, or other matters related to NHSE. This is because NHSE is required by statute to exercise functions of the Secretary of State in respect of Training and workforce planning. If you prefer, you can opt out of receiving information about general matters of policy impacting on Training and workforce planning by contacting your Local Office recruitment lead or tel@hee.nhs.uk. The relevant Local Office or a representative from the relevant training platform website will provide you with further advice and guidance regarding any consequences of your request.
    NHSE will not send you generic information from other public authorities and government agencies on issues of government policy.

9 TRANSFERS ABROAD

    The UK GDPR imposes restrictions on the transfer of personal data outside the European Union, to third countries or international organisations, in order to ensure that the level of protection of individuals afforded by the UK GDPR is not undermined.
    Your data may only be transferred abroad where NHSE is assured that a third country, a territory or one or more specific sectors in the third country, or an international organisation ensures an adequate level of protection.

10 HOW WE PROTECT YOUR PERSONAL DATA

    Our processing of all personal data complies with the UK GDPR principles. We have put in place appropriate security measures to prevent your personal data from being accidentally lost, used or accessed in an unauthorised way, altered or disclosed. The security of the data is assured through the implementation of NHSE’s policies on information governance management.
    The personal data we hold may be held as an electronic record on data systems managed by NHSE, or as a paper record. These records are only accessed, seen and used in the following circumstances:
  1. if required and/or permitted by law; or
  2. by NHSE staff who need access to them so they can do their jobs and who are subject to a duty of confidentiality; or
  3. by other partner organisations, including our suppliers, who have been asked to sign appropriate non-disclosure or data sharing agreements and will never be allowed to use the information for commercial purposes.
    We make every effort to keep your personal information accurate and up to date, but in some cases we are reliant on you as the data subject to notify us of any necessary changes to your personal data. If you tell us of any changes in your circumstances, we can update the records with personal data you choose to share with us.
    Information collected by NHSE will never be sold for financial gain or shared with other organisations for commercial purposes.
    We have put in place procedures to deal with any suspected personal data breach and will notify you and any applicable regulator of a breach where we are legally required to do so.

11 SHARING PERSONAL DATA

    So we can provide the right services at the right level, we may share your personal data within services across NHSE and with other third party organisations such as the Department of Health, higher education institutions, clinical placement providers, colleges, faculties, other NHSE Local Offices, the General Medical Council, NHS Trusts/Health Boards/Health and Social Care Trusts, approved academic researchers and other NHS and government agencies where necessary, to provide the best possible Training and to ensure that we discharge NHSEs responsibilities for employment and workforce planning for the NHS. This will be on a legitimate need to know basis only.
    We may also share information, where necessary, to prevent, detect or assist in the investigation of fraud or criminal activity, to assist in the administration of justice, for the purposes of seeking legal advice or exercising or defending legal rights or as otherwise required by the law.
    Where the data is used for analysis and publication by a recipient or third party, any publication will be on an anonymous basis, and will not make it possible to identify any individual. This will mean that the data ceases to become personal data.

12 HOW LONG WE RETAIN YOUR PERSONAL DATA

    We will keep personal data for no longer than necessary to fulfil the purposes we collected it for, in accordance with our records management policy and the NHS records retention schedule within the NHS Records Management Code of Practice at: https://www.england.nhs.uk/contact-us/privacy-notice/nhs-england-as-a-data-controller (as may be amended from time to time).
    In some circumstances you can ask us to delete your data. Please see the “Your rights” section below for further information.
    In some circumstances we will anonymise your personal data (so that it can no longer be associated with you) for research or statistical purposes, in which case we may use this information indefinitely without further notice to you.

13 OPEN DATA

    Open data is data that is published by central government, local authorities and public bodies to help you build products and services. NHSE policy is to observe the Cabinet Office transparency and accountability commitments towards more open use of public data in accordance with relevant and applicable UK legislation.
    NHSE would never share personal data through the open data facility. To this end, NHSE will implement information governance protocols that reflect the ICO’s recommended best practice for record anonymisation, and Office of National Statistics guidance on publication of statistical information.

14 YOUR RIGHTS

14.1 Right to rectification and erasure

    Under the UK GDPR you have the right to rectification of inaccurate personal data and the right to request the erasure of your personal data. However, the right to erasure is not an absolute right and it may be that it is necessary for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.

14.2 Right to object and withdraw your consent

    You have the right in certain circumstances to ask NHSE to stop processing your personal data in relation to any NHSE service. As set out above, you can decide that you do not wish to receive information from NHSE about matters of policy affecting Training and workforce. However, the right to object is not an absolute right and it may be that it is necessary in certain circumstances for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.
    If you object to the way in which NHSE is processing your personal information or if you wish to ask NHSE to stop processing your personal data, please contact your relevant Local Office.
    Please note, if we do stop processing personal data about you, this may prevent NHSE from providing the best possible service to you. Withdrawing your consent will result in your Training account being anonymised and access to the Training removed.

14.3 Right to request access

    You can access a copy of the information NHSE holds about you by writing to NHSE’s Public and Parliamentary Accountability Team. This information is generally available to you free of charge subject to the receipt of appropriate identification. More information about subject access requests can be found here: https://www.hee.nhs.uk/about/contact-us/subject-access-request.

14.4 Right to request a transfer

    The UK GDPR sets out the right for a data subject to have their personal data ported from one controller to another on request in certain circumstances. You should discuss any request for this with your Local Office. This right only applies to automated information which you initially provided consent for us to use or where we used the information to perform a contract with you.

14.5 Right to restrict processing

    You can ask us to suspend the processing of your personal data if you want us to establish the data’s accuracy, where our use of the data is unlawful but you do not want us to erase it, where you need us to hold the data even if we no longer require it as you need it to establish, exercise or defend legal claims or where you have objected to our use of your data but we need to verify whether we have overriding legitimate grounds to use it.

14.6 Complaints

    You have the right to make a complaint at any time to the ICO. We would, however, appreciate the chance to deal with your concerns before you approach the ICO so please contact your Local Office or the DPO in the first instance, using the contact details above.
    You can contact the ICO at the following address:

    The Office of the Information Commissioner
    Wycliffe House
    Water Lane
    Wilmslow
    Cheshire
    SK9 5AF

14.7 Your responsibilities

    It is important that you work with us to ensure that the information we hold about you is accurate and up to date so please inform NHSE if any of your personal data needs to be updated or corrected.
    All communications from NHSE will normally be by email. It is therefore essential for you to maintain an effective and secure email address, or you may not receive information or other important news and information about your employment or Training.
"; + + Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + PrivacyPolicyOld + "'" + + "where ConfigName='PrivacyPolicy' AND IsHtml = 1"); + + var AcceptableUseOld = @"

ACCEPTABLE USE POLICY

+
    +
  1. + General +
      +
    1. This Acceptable Use Policy sets out how we permit you to use any of our Platforms. Your compliance with this Acceptable Use Policy is a condition of your use of the Platform.
    2. +
    3. Capitalised terms have the meaning given to them in the terms of use for the Platform which are available at https://www.dls.nhs.uk/v2/LearningSolutions/Terms.
    4. +
    +
  2. +
  3. + Acceptable use +
      +
    1. You are permitted to use the Platform as set out in the Terms and for the purpose of personal study.
    2. +
    3. You must not use any part of the Content on the Platform for commercial purposes without obtaining a licence to do so from us or our licensors.
    4. +
    5. If you print off, copy, download, share or repost any part of the Platform in breach of this Acceptable Use Policy, your right to use the Platform will cease immediately and you must, at our option, return or destroy any copies of the materials you have made.
    6. +
    7. Our status (and that of any identified contributors) as the authors of Content on the Platform must always be acknowledged (except in respect of Third-Party Content).
    8. +
    +
  4. +
  5. + Prohibited uses +
      +
    1. + You may not use the Platform: +
        +
      1. in any way that breaches any applicable local, national or international law or regulation;
      2. +
      3. in any way that is unlawful or fraudulent or has any unlawful or fraudulent purpose or effect;
      4. +
      5. in any way that infringes the rights of, or restricts or inhibits the use and enjoyment of this site by any third party;
      6. +
      7. for the purpose of harming or attempting to harm minors in any way;
      8. +
      9. to bully, insult, intimidate or humiliate any person;
      10. +
      11. to send, knowingly receive, upload, download, use or re-use any material which does not comply with our Content Standards as set out in paragraph 4;
      12. +
      13. to transmit, or procure the sending of, any unsolicited or unauthorised advertising or promotional material or any other form of similar solicitation (spam), or any unwanted or repetitive content that may cause disruption to the Platform or diminish the user experience, of the Platform’s usefulness or relevant to others;
      14. +
      15. to do any act or thing with the intention of disrupting the Platform in any way, including uploading any malware or links to malware, or introduce any virus, trojan, worm, logic bomb or other material that is malicious or technologically harmful or other potentially damaging items into the Platform;
      16. +
      17. to knowingly transmit any data, send or upload any material that contains viruses, Trojan horses, worms, time-bombs, keystroke loggers, spyware, adware or any other harmful programs or similar computer code designed to adversely affect the operation of any computer software or hardware; or
      18. +
      19. to upload terrorist content.
      20. +
      +
    2. +
    3. + You also agree: +
        +
      1. to follow any reasonable instructions given to you by us in connection with your use of the Platform;
      2. +
      3. to respect the rights and dignity of others, in order to maintain the ethos and good reputation of the NHS, the public good generally and the spirit of cooperation between those studying and working within the health and care sector. In particular, you must act in a professional manner with regard to all other users of the Platform at all times;
      4. +
      5. + not to modify or attempt to modify any of the Content, save: +
          +
        1. in respect of Contributions;
        2. +
        3. where you are the editor of a catalogue within the Learning Hub, you may alter Content within that catalogue;
        4. +
        +
      6. +
      7. not to download or copy any of the Content to electronic or photographic media;
      8. +
      9. not to reproduce any part of the Content by any means or under any format other than as a reasonable aid to your personal study;
      10. +
      11. not to reproduce, duplicate, copy or re-sell any Content in contravention of the provisions of this Acceptable Use Policy; and
      12. +
      13. not to use tools that automatically perform actions on your behalf;
      14. +
      15. not to upload any content that infringes the intellectual property rights, privacy rights or any other rights of any person or organisation; and
      16. +
      17. not to attempt to disguise your identity or that of your organisation;
      18. +
      19. + not to access without authority, interfere with, damage or disrupt: +
          +
        1. any part of the Platform;
        2. +
        3. any equipment or network on which the Platform is stored;
        4. +
        5. any software used in the provision of the Platform;
        6. +
        7. the server on which the Platform is stored;
        8. +
        9. any computer or database connected to the Platform; or
        10. +
        11. any equipment or network or software owned or used by any third party.
        12. +
        +
      20. +
      21. not to attack the Platform via a denial-of-service attack or a distributed denial-of-service attack.
      22. +
      +
    4. +
    +
  6. +
  7. + Content standards +
      +
    1. The content standards set out in this paragraph 4 (Content Standards) apply to any and all Contributions.
    2. +
    3. The Content Standards must be complied with in spirit as well as to the letter. The Content Standards apply to each part of any Contribution as well as to its whole.
    4. +
    5. We will determine, in our discretion, whether a Contribution breaches the Content Standards.
    6. +
    7. + A Contribution must: +
        +
      1. be accurate (where it states facts);
      2. +
      3. be genuinely held (where it states opinions); and
      4. +
      5. comply with the law applicable in England and Wales and in any country from which it is posted.
      6. +
      +
    8. +
    9. + A Contribution must not: +
        +
      1. + contain misinformation that is likely to harm users, patients/service users, health and care workers, or the general public’s wellbeing, safety, trust and reputation, including the reputation of the NHS or any part of it. This could include false and misleading information relating to disease prevention and treatment, conspiracy theories, content that encourages discrimination, harassment or physical violence, content originating from misinformation campaigns, and content edited or manipulated in such a way as to constitute misinformation; +
      2. +
      3. + contain any content or link to any content: +
          +
        1. which is created for advertising, promotional or other commercial purposes, including links, logos and business names;
        2. +
        3. which requires a subscription or payment to gain access to such content;
        4. +
        5. in which the user has a commercial interest;
        6. +
        7. which promotes a business name and/or logo;
        8. +
        9. which contains a link to an app via iOS or Google Play; or
        10. +
        11. which has as its purpose or effect the collection and sharing of personal data;
        12. +
        +
      4. +
      5. + be irrelevant to the purpose or aims of the Platform or while addressing relevant subject matter, contain an irrelevant, unsuitable or inappropriate slant (for example relating to potentially controversial opinions or beliefs of any kind intended to influence others); +
      6. +
      7. be defamatory of any person;
      8. +
      9. be obscene, offensive, hateful or inflammatory, or contain any profanity;
      10. +
      11. bully, insult, intimidate or humiliate;
      12. +
      13. + encourage suicide, substance abuse, eating disorders or other acts of self-harm.Content related to self - harm for the purposes of therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally; +
      14. +
      15. + feature sexual imagery purely intended to stimulate sexual arousal. Non - pornographic content relating to sexual health and related issues, surgical procedures and the results of surgical procedures, breastfeeding, therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally; +
      16. +
      17. + include child sexual abuse material. Content relating to safeguarding which addresses the subject of child sexual abuse may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally; +
      18. +
      19. + incite or glorify violence including content designed principally for the purposes of causing reactions of shock or disgust; +
      20. +
      21. + promote discrimination or discriminate in respect of the protected characteristics set out in the Equality Act 2010, being age, disability, gender reassignment, marriage and civil partnership, pregnancy and maternity, race, nationality, religion or belief, sex, and sexual orientation; +
      22. +
      23. infringe any copyright, database right or trade mark of any other person;
      24. +
      25. be likely to deceive any person;
      26. +
      27. breach any legal duty owed to a third party, such as a contractual duty or a duty of confidence;
      28. +
      29. + promote any illegal content or activity, including but not limited to the encouragement, promotion, justification, praise or provision of aid to dangerous persons or organisations, including extremists, terrorists and terrorist organisations and those engaged in any form of criminal activity; +
      30. +
      31. be in contempt of court;
      32. +
      33. + be threatening, abuse or invade another''s privacy, or cause annoyance, inconvenience or needless anxiety; +
      34. +
      35. be likely to harass, bully, shame, degrade, upset, embarrass, alarm or annoy any other person;
      36. +
      37. impersonate any person or misrepresent your identity or affiliation with any person;
      38. +
      39. + advocate, promote, incite any party to commit, or assist any unlawful or criminal act such as (by way of example only) copyright infringement or computer misuse; +
      40. +
      41. + contain a statement which you know or believe, or have reasonable grounds for believing, that members of the public to whom the statement is, or is to be, published are likely to understand as a direct or indirect encouragement or other inducement to the commission, preparation or instigation of acts of terrorism; +
      42. +
      43. contain harmful material;
      44. +
      45. give the impression that the Contribution emanates from us, if this is not the case; or
      46. +
      47. disclose any third party’s confidential information, identity, personally identifiable information or personal data (including data concerning health).
      48. +
      +
    10. +
    11. You acknowledge and accept that, when using the Platform and accessing the Content, some Content that has been uploaded by third parties may be factually inaccurate, or the topics addressed by the Content may be offensive, indecent, or objectionable in nature. We are not responsible (legally or otherwise) for any claim you may have in relation to the Content.
    12. +
    13. When producing Content to upload to the Platform, we encourage you to implement NICE guideline recommendations and ensure that sources of evidence are valid (for example, by peer review).
    14. +
    +
  8. +
  9. + Metadata +
      + When making any Contribution, you must where prompted include a sufficient description of the Content so that other users can understand the description, source, and age of the Content. For example, if Content has been quality assured, then the relevant information should be posted in the appropriate field. All metadata fields on the Platform must be completed appropriately before initiating upload. Including the correct information is important in order to help other users locate the Content (otherwise the Content may not appear in search results for others to select). +
    +
  10. +
  11. + Updates +
      + You must update each Contribution at least once every 3 (three) years, or update or remove it should it cease to be relevant or become outdated or revealed or generally perceived to be unsafe or otherwise unsuitable for inclusion on the Platform. +
    +
  12. +
  13. + Accessibility +
      + Where practicable, all Contributions should aim to meet the accessibility standards as described in our Accessibility Statement + [https://www.dls.nhs.uk/v2/LearningSolutions/AccessibilityHelp] and as set out in the AA Standard Web Content Accessibility Guidelines v2.1 found here: + https://www.w3.org/TR/WCAG21/. +
    +
  14. +
  15. + Rules about linking to the Platform +
      +
    1. The Platform must not be framed on any other site.
    2. +
    3. You may directly link to any Content that is hosted on the Platform, however, please be aware that not all links will continue to be available indefinitely. We will use our best efforts to ensure that all links are valid at the time of creating the related Content but cannot be held responsible for any subsequent changes to the link address or related Content.
    4. +
    +
  16. +
  17. + No text or data mining, or web scraping +
      +
    1. + You shall not conduct, facilitate, authorize or permit any text or data mining or web scraping in relation to the Platform or any services provided via, or in relation to, the Platform. This includes using (or permitting, authorizing or attempting the use of): +
        +
      1. any ""robot"", ""bot"", ""spider"", ""scraper"" or other automated device, program, tool, algorithm, code, process or methodology to access, obtain, copy, monitor or republish any portion of the Platform or any data, Content, information or services accessed via the same; and/or
      2. +
      3. any automated analytical technique aimed at analyzing text and data in digital form to generate information which includes but is not limited to patterns, trends, and correlations.
      4. +
      +
    2. +
    3. The provisions in this paragraph should be treated as an express reservation of our rights in this regard, including for the purposes of Article 4(3) of Digital Copyright Directive ((EU) 2019/790).
    4. +
    5. This paragraph shall not apply insofar as (but only to the extent that) we are unable to exclude or limit text or data mining or web scraping activity by contract under the laws which are applicable to us.
    6. +
    +
  18. +
  19. + Breach of this Acceptable Use Policy +
      + Failure to comply with this Acceptable Use Policy constitutes a material breach of this Acceptable Use Policy upon which you are permitted to use the Platform and may result in our taking all or any of the following actions: +
    1. immediate, temporary, or permanent withdrawal of your right to use the Platform;
    2. +
    3. immediate, temporary, or permanent removal of any Contribution uploaded by you to the Platform;
    4. +
    5. issue of a warning to you;
    6. +
    7. legal proceedings against you for reimbursement of all costs on an indemnity basis (including, but not limited to, reasonable administrative and legal costs) resulting from the breach, and/or further legal action against you;
    8. +
    9. disclosure of such information to law enforcement authorities as we reasonably feel is necessary or as required by law; and/or
    10. +
    11. any other action we reasonably deem appropriate.
    12. +
    +
  20. +
"; + + Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + AcceptableUseOld + "'" + + "where ConfigName='AcceptableUse' AND IsHtml = 1"); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202508181154_CreateOrAlterMoveCompetenciesAndGroups.cs b/DigitalLearningSolutions.Data.Migrations/202508181154_CreateOrAlterMoveCompetenciesAndGroups.cs new file mode 100644 index 0000000000..60ac1b3cfb --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202508181154_CreateOrAlterMoveCompetenciesAndGroups.cs @@ -0,0 +1,19 @@ +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + [Migration(202508181154)] + public class CreateOrAlterMoveCompetenciesAndGroups : Migration + { + public override void Up() + { + Execute.Sql(Properties.Resources.TD_483_uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP); + Execute.Sql(Properties.Resources.TD_483_uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP); + } + public override void Down() + { + Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_MoveCompetencyGroupInSelfAssessment]"); + Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_MoveCompetencyInSelfAssessment]"); + Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_RenumberSelfAssessmentStructure]"); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202510021000_AddIsdeletedSelfAssessmentCollaborators.cs b/DigitalLearningSolutions.Data.Migrations/202510021000_AddIsdeletedSelfAssessmentCollaborators.cs new file mode 100644 index 0000000000..870707eb79 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202510021000_AddIsdeletedSelfAssessmentCollaborators.cs @@ -0,0 +1,16 @@ +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + [Migration(202510021000)] + public class AddIsdeletedSelfAssessmentCollaborators : Migration + { + public override void Up() + { + Alter.Table("SelfAssessmentCollaborators").AddColumn("IsDeleted").AsBoolean().WithDefaultValue(false); + } + public override void Down() + { + Delete.Column("IsDeleted").FromTable("SelfAssessmentCollaborators"); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202510221132_AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable.cs b/DigitalLearningSolutions.Data.Migrations/202510221132_AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable.cs new file mode 100644 index 0000000000..fbb0e4ee35 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202510221132_AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable.cs @@ -0,0 +1,18 @@ +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + + [Migration(202510221132)] + public class AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable : Migration + { + public override void Up() + { + Alter.Table("SelfAssessments").AddColumn("IncludeLearnerDeclarationPrompt").AsBoolean().WithDefaultValue(false); + } + + public override void Down() + { + Delete.Column("IncludeLearnerDeclarationPrompt").FromTable("SelfAssessments"); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202511241125_Alter_usp_MoveCompetencyInSelfAssessment.cs b/DigitalLearningSolutions.Data.Migrations/202511241125_Alter_usp_MoveCompetencyInSelfAssessment.cs new file mode 100644 index 0000000000..691a82b9ee --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202511241125_Alter_usp_MoveCompetencyInSelfAssessment.cs @@ -0,0 +1,19 @@ + + +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + + [Migration(202511241125)] + public class Alter_usp_MoveCompetencyInSelfAssessment : Migration + { + public override void Up() + { + Execute.Sql(Properties.Resources.TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Up); + } + public override void Down() + { + Execute.Sql(Properties.Resources.TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Down); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202512020916_Alter_usp_GetSelfAssessmentReport.cs b/DigitalLearningSolutions.Data.Migrations/202512020916_Alter_usp_GetSelfAssessmentReport.cs new file mode 100644 index 0000000000..b57ed8037b --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202512020916_Alter_usp_GetSelfAssessmentReport.cs @@ -0,0 +1,19 @@ + + +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + + [Migration(202512020916)] + public class Alter_usp_GetSelfAssessmentReport : Migration + { + public override void Up() + { + Execute.Sql(Properties.Resources.TD_6437_usp_GetSelfAssessmentReport_Up); + } + public override void Down() + { + Execute.Sql(Properties.Resources.TD_6437_usp_GetSelfAssessmentReport_Down); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202601091000_SupervisorDashboardIndexes.cs b/DigitalLearningSolutions.Data.Migrations/202601091000_SupervisorDashboardIndexes.cs new file mode 100644 index 0000000000..52faab12b0 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202601091000_SupervisorDashboardIndexes.cs @@ -0,0 +1,71 @@ +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + using FluentMigrator.SqlServer; + + [Migration(202601091000)] + public class SupervisorDashboardIndexes : Migration + { + public override void Up() + { + Create.Index("IX_SupervisorDelegates_Admin_Removed") + .OnTable("SupervisorDelegates") + .OnColumn("SupervisorAdminID").Ascending() + .OnColumn("Removed").Ascending() + .WithOptions() + .NonClustered() + .Include("DelegateUserID"); + + Create.Index("IX_CandidateAssessmentSupervisors_SupervisorDelegateId_Removed") + .OnTable("CandidateAssessmentSupervisors") + .OnColumn("SupervisorDelegateId").Ascending() + .OnColumn("Removed").Ascending() + .WithOptions() + .NonClustered() + .Include("CandidateAssessmentID"); + + Create.Index("IX_CandidateAssessments_RemovedDate") + .OnTable("CandidateAssessments") + .OnColumn("RemovedDate").Ascending() + .WithOptions() + .NonClustered() + .Include("SelfAssessmentID") + .Include("DelegateUserID"); + + Create.Index("IX_CandidateAssessmentSupervisorVerifications_CandidateAssessmentSupervisorID_Verified") + .OnTable("CandidateAssessmentSupervisorVerifications") + .OnColumn("CandidateAssessmentSupervisorID").Ascending() + .OnColumn("Verified").Ascending() + .WithOptions() + .NonClustered(); + + Create.Index("IX_SRSV_CandidateAssessmentSupervisorID_SelfAssessmentResultId_Superceded_Verified") + .OnTable("SelfAssessmentResultSupervisorVerifications") + .OnColumn("CandidateAssessmentSupervisorID").Ascending() + .OnColumn("SelfAssessmentResultId").Ascending() + .OnColumn("Superceded").Ascending() + .OnColumn("Verified").Ascending() + .WithOptions() + .NonClustered() + .Include("Requested"); + + Create.Index("IX_SelfAssessmentResults_SelfAssessmentID_Result") + .OnTable("SelfAssessmentResults") + .OnColumn("SelfAssessmentID").Ascending() + .OnColumn("Result").Ascending() + .WithOptions() + .NonClustered() + .Include("CompetencyID") + .Include("DateTime"); + } + public override void Down() + { + Delete.Index("IX_SupervisorDelegates_Admin_Removed").OnTable("SupervisorDelegates"); + Delete.Index("IX_CandidateAssessmentSupervisors_SupervisorDelegateId_Removed").OnTable("CandidateAssessmentSupervisors"); + Delete.Index("IX_CandidateAssessments_RemovedDate").OnTable("CandidateAssessments"); + Delete.Index("IX_CASVerifications_CandidateAssessmentSupervisorID_Verified").OnTable("CandidateAssessmentSupervisorVerifications"); + Delete.Index("IX_SRSV_CandidateAssessmentSupervisorID_SelfAssessmentResultId_Superceded_Verified").OnTable("SelfAssessmentResultSupervisorVerifications"); + Delete.Index("IX_SelfAssessmentResults_SelfAssessmentID_Result").OnTable("SelfAssessmentResults"); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/202602091540_AlterViewAdminUsersAddAdminUserId.cs b/DigitalLearningSolutions.Data.Migrations/202602091540_AlterViewAdminUsersAddAdminUserId.cs new file mode 100644 index 0000000000..5287079d7b --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202602091540_AlterViewAdminUsersAddAdminUserId.cs @@ -0,0 +1,17 @@ +using FluentMigrator; + +namespace DigitalLearningSolutions.Data.Migrations +{ + [Migration(202602091540)] + public class AlterViewAdminUsersAddAdminUserId : Migration + { + public override void Up() + { + Execute.Sql(Properties.Resources.TD_6866_AlterViewAdminUsersAddAdminUserId_Up); + } + public override void Down() + { + Execute.Sql(Properties.Resources.TD_6866_AlterViewAdminUsersAddAdminUserId_Down); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs index ec509516b1..51adc5e370 100644 --- a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs +++ b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs @@ -2237,6 +2237,116 @@ internal static string TD_4634_Alter_GetCompletedCoursesForCandidate_UP { } } + /// + /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[usp_MoveCompetencyInSelfAssessment] Script Date: 24/11/2025 10:56:08 ******/ + ///SET ANSI_NULLS ON + ///GO + /// + ///SET QUOTED_IDENTIFIER ON + ///GO + /// + ///ALTER PROCEDURE [dbo].[usp_MoveCompetencyInSelfAssessment] + /// @SelfAssessmentID INT, + /// @CompetencyID INT, + /// @Direction NVARCHAR(10) + ///AS + ///BEGIN + /// SET NOCOUNT ON; + /// + /// DECLARE @GroupID INT, @CurrentOrder INT; + /// + /// SELECT + /// @GroupID = CompetencyGroupID, + /// @CurrentOrder = Ordering + /// FROM SelfAssessment [rest of string was truncated]";. + /// + internal static string TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Down { + get { + return ResourceManager.GetString("TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Down", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[usp_MoveCompetencyInSelfAssessment] Script Date: 24/11/2025 10:56:08 ******/ + ///SET ANSI_NULLS ON + ///GO + /// + ///SET QUOTED_IDENTIFIER ON + ///GO + /// + ///ALTER PROCEDURE [dbo].[usp_MoveCompetencyInSelfAssessment] + /// @SelfAssessmentID INT, + /// @CompetencyID INT, + /// @Direction NVARCHAR(10) + ///AS + ///BEGIN + /// SET NOCOUNT ON; + /// + /// DECLARE @CurrentOrder INT; + /// + /// SELECT + /// @CurrentOrder = Ordering + /// FROM SelfAssessmentStructure + /// WHERE SelfAssessmentID = @SelfAssessme [rest of string was truncated]";. + /// + internal static string TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Up { + get { + return ResourceManager.GetString("TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Up", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to + ///CREATE OR ALTER PROCEDURE usp_RenumberSelfAssessmentStructure + /// @SelfAssessmentID INT + ///AS + ///BEGIN + /// SET NOCOUNT ON; + /// + /// /* + /// Step 1: Build an ordered list of groups + /// - Each group is ranked by its current Min(Ordering) + /// - Ungrouped competencies (NULL CompetencyGroupID) are treated as their own "pseudo group" + /// */ + /// ;WITH GroupRanks AS ( + /// SELECT + /// CompetencyGroupID, + /// ROW_NUMBER() OVER (ORDER BY MIN(Ordering)) AS GroupRank + /// FROM Sel [rest of string was truncated]";. + /// + internal static string TD_483_uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP { + get { + return ResourceManager.GetString("TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CREATE OR ALTER PROCEDURE usp_MoveCompetencyInSelfAssessment + /// @SelfAssessmentID INT, + /// @CompetencyID INT, + /// @Direction NVARCHAR(10) + ///AS + ///BEGIN + /// SET NOCOUNT ON; + /// + /// DECLARE @GroupID INT, @CurrentOrder INT; + /// + /// SELECT + /// @GroupID = CompetencyGroupID, + /// @CurrentOrder = Ordering + /// FROM SelfAssessmentStructure + /// WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID; + /// + /// IF @GroupID IS NULL + /// BEGIN + /// -- Can't reorder ungrouped competencies [rest of string was truncated]";. + /// + internal static string TD_483_uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP { + get { + return ResourceManager.GetString("TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP", resourceCulture); + } + } + /// /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[GetActivitiesForDelegateEnrolment] Script Date: 22/10/2024 16:55:08 ******/ ///SET ANSI_NULLS ON @@ -2717,6 +2827,112 @@ internal static string TD_5759_CreateOrAlterSelfAssessmentReportSPandTVF_UP { } } + /// + /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[usp_GetSelfAssessmentReport] Script Date: 01/12/2025 20:54:19 ******/ + ///SET ANSI_NULLS ON + ///GO + /// + ///SET QUOTED_IDENTIFIER ON + ///GO + /// + ///ALTER PROCEDURE [dbo].[usp_GetSelfAssessmentReport] + /// @SelfAssessmentID INT, + /// @CentreID INT + ///AS + ///BEGIN + /// SET NOCOUNT ON; + /// + /// -- Step 1: Materialize the LatestAssessmentResults into a temp table + /// IF OBJECT_ID('tempdb..#LatestAssessmentResults') IS NOT NULL + /// DROP TABLE #LatestAssessmentResults; + /// + /// SELECT + /// [rest of string was truncated]";. + /// + internal static string TD_6437_usp_GetSelfAssessmentReport_Down { + get { + return ResourceManager.GetString("TD_6437_usp_GetSelfAssessmentReport_Down", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[usp_GetSelfAssessmentReport] Script Date: 01/12/2025 20:54:19 ******/ + ///SET ANSI_NULLS ON + ///GO + /// + ///SET QUOTED_IDENTIFIER ON + ///GO + /// + ///ALTER PROCEDURE [dbo].[usp_GetSelfAssessmentReport] + /// @SelfAssessmentID INT, + /// @CentreID INT + ///AS + ///BEGIN + /// SET NOCOUNT ON; + /// + /// -- Step 1: Materialize the LatestAssessmentResults into a temp table + /// IF OBJECT_ID('tempdb..#LatestAssessmentResults') IS NOT NULL + /// DROP TABLE #LatestAssessmentResults; + /// + /// SELECT + /// [rest of string was truncated]";. + /// + internal static string TD_6437_usp_GetSelfAssessmentReport_Up { + get { + return ResourceManager.GetString("TD_6437_usp_GetSelfAssessmentReport_Up", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/ + ///SET ANSI_NULLS ON + ///GO + /// + ///SET QUOTED_IDENTIFIER ON + ///GO + /// + ///-- ============================================= + ///-- Author: + ///-- Modified date: 02/06/2023 + ///-- Description: Return the admin user details + ///-- ============================================= + /// + ///ALTER VIEW [dbo].[AdminUsers] AS + ///SELECT dbo.AdminAccounts.ID AS AdminID, + /// null AS Login, + /// dbo.Users.Passwor [rest of string was truncated]";. + /// + internal static string TD_6866_AlterViewAdminUsersAddAdminUserId_Down { + get { + return ResourceManager.GetString("TD_6866_AlterViewAdminUsersAddAdminUserId_Down", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/ + ///SET ANSI_NULLS ON + ///GO + /// + ///SET QUOTED_IDENTIFIER ON + ///GO + /// + ///-- ============================================= + ///-- Author: + ///-- Modified date: 02/06/2023 + ///-- Description: Return the admin user details + ///-- ============================================= + /// + ///ALTER VIEW [dbo].[AdminUsers] AS + ///SELECT dbo.AdminAccounts.ID AS AdminID, + /// dbo.AdminAccounts.UserID AS AdminUserID, + /// null [rest of string was truncated]";. + /// + internal static string TD_6866_AlterViewAdminUsersAddAdminUserId_Up { + get { + return ResourceManager.GetString("TD_6866_AlterViewAdminUsersAddAdminUserId_Up", resourceCulture); + } + } + /// /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[GetActiveAvailableCustomisationsForCentreFiltered_V6] Script Date: 29/09/2022 19:11:04 ******/ ///SET ANSI_NULLS ON diff --git a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx index bb42e0ec70..c71a4e2d2b 100644 --- a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx +++ b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx @@ -499,6 +499,12 @@ ..\Scripts\TD-5759_CreateOrAlterSelfAssessmentReportSPandTVF-Fix_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + ..\Scripts\TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + ..\Scripts\TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + ..\Scripts\TD-5535-Alter_GetActivitiesForDelegateEnrolment_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 @@ -514,4 +520,22 @@ ..\Scripts\TD-5552-Alter_SendRetiringSelfAssessmentNotification_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + + ..\Scripts\TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + + + ..\Scripts\TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + + + ..\Scripts\TD-6437-usp_GetSelfAssessmentReport_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + + + ..\Scripts\TD-6437-usp_GetSelfAssessmentReport_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + + + ..\Scripts\TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + + ..\Scripts\TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + \ No newline at end of file diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql new file mode 100644 index 0000000000..0b6668e913 Binary files /dev/null and b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql differ diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql new file mode 100644 index 0000000000..50e97004f2 Binary files /dev/null and b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql differ diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql new file mode 100644 index 0000000000..f42d8894c7 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql @@ -0,0 +1,164 @@ + +CREATE OR ALTER PROCEDURE usp_RenumberSelfAssessmentStructure + @SelfAssessmentID INT +AS +BEGIN + SET NOCOUNT ON; + + /* + Step 1: Build an ordered list of groups + - Each group is ranked by its current Min(Ordering) + - Ungrouped competencies (NULL CompetencyGroupID) are treated as their own "pseudo group" + */ + ;WITH GroupRanks AS ( + SELECT + CompetencyGroupID, + ROW_NUMBER() OVER (ORDER BY MIN(Ordering)) AS GroupRank + FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @SelfAssessmentID + GROUP BY CompetencyGroupID + ) + /* + Step 2: Renumber groups and competencies + - Groups get ranked (1,2,3) + - Within each group, competencies are ordered by their current Ordering and renumbered (1,2,3) + */ + UPDATE sas + SET sas.Ordering = rn.NewOrdering + FROM SelfAssessmentStructure sas + INNER JOIN ( + SELECT + s.ID, + -- new group position * 1000 + row within group + -- gives room between groups and avoids clashes + (g.GroupRank * 1000) + + ROW_NUMBER() OVER (PARTITION BY s.CompetencyGroupID, g.GroupRank ORDER BY s.Ordering, s.ID) AS NewOrdering + FROM SelfAssessmentStructure s + INNER JOIN GroupRanks g + ON g.CompetencyGroupID = s.CompetencyGroupID + WHERE s.SelfAssessmentID = @SelfAssessmentID + ) rn ON sas.ID = rn.ID; + +END + +GO + +CREATE OR ALTER PROCEDURE usp_MoveCompetencyGroupInSelfAssessment + @SelfAssessmentID INT, + @GroupID INT, + @Direction NVARCHAR(10) -- 'up' or 'down' +AS +BEGIN + SET NOCOUNT ON; + SET XACT_ABORT ON; + + BEGIN TRY + BEGIN TRAN; + + /* 1) Rank groups by current Min(Ordering) (NULL groups excluded here; include if desired). */ + ;WITH GroupRanks AS ( + SELECT + CompetencyGroupID, + MIN(Ordering) AS MinOrder, + ROW_NUMBER() OVER ( + ORDER BY MIN(Ordering), MIN(CompetencyGroupID) + ) AS RankPos + FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @SelfAssessmentID + AND CompetencyGroupID IS NOT NULL + GROUP BY CompetencyGroupID + ) + SELECT * + INTO #Groups + FROM GroupRanks; + + DECLARE @CurRank INT, @SwapRank INT, @SwapGroupID INT; + + SELECT @CurRank = RankPos + FROM #Groups + WHERE CompetencyGroupID = @GroupID; + + IF @CurRank IS NULL + BEGIN + DROP TABLE #Groups; + COMMIT TRAN; RETURN; -- nothing to do + END + + IF LOWER(@Direction) = 'up' + SET @SwapRank = @CurRank - 1; + ELSE IF LOWER(@Direction) = 'down' + SET @SwapRank = @CurRank + 1; + ELSE + BEGIN + DROP TABLE #Groups; + ROLLBACK TRAN; THROW 50000, 'Direction must be ''up'' or ''down''.', 1; + END + + SELECT @SwapGroupID = CompetencyGroupID + FROM #Groups + WHERE RankPos = @SwapRank; + + IF @SwapGroupID IS NULL + BEGIN + DROP TABLE #Groups; + COMMIT TRAN; RETURN; -- already at top/bottom + END + + /* 2) Build a mapping where ONLY the two groups swap ranks; others keep theirs. */ + SELECT + g.CompetencyGroupID, + CASE + WHEN g.CompetencyGroupID = @GroupID THEN @SwapRank + WHEN g.CompetencyGroupID = @SwapGroupID THEN @CurRank + ELSE g.RankPos + END AS NewRank + INTO #RankMap + FROM #Groups g; + + /* 3) Choose a block size big enough to keep groups separated when we recompute. + Using count(rows in this self assessment) + 10 is a safe dynamic choice. */ + DECLARE @Block INT = + (SELECT COUNT(*) FROM SelfAssessmentStructure WHERE SelfAssessmentID = @SelfAssessmentID) + 10; + + /* 4) Recompute EVERY rows Ordering from the rank map (this sets the new global order). + We preserve within-group relative order using the existing Ordering (and ID as tiebreak). */ + ;WITH NewOrders AS ( + SELECT + s.ID, + (m.NewRank * @Block) + + ROW_NUMBER() OVER ( + PARTITION BY s.CompetencyGroupID + ORDER BY s.Ordering, s.ID + ) AS NewOrdering + FROM SelfAssessmentStructure s + JOIN #RankMap m + ON m.CompetencyGroupID = s.CompetencyGroupID + WHERE s.SelfAssessmentID = @SelfAssessmentID + ) + UPDATE s + SET s.Ordering = n.NewOrdering + FROM SelfAssessmentStructure s + JOIN NewOrders n ON n.ID = s.ID; + + /* 5) (Optional) Compress to 1..N while keeping the just-established relative order. */ + ;WITH Ordered AS ( + SELECT ID, + ROW_NUMBER() OVER (ORDER BY Ordering, ID) AS Seq + FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @SelfAssessmentID + ) + UPDATE s + SET s.Ordering = o.Seq + FROM SelfAssessmentStructure s + JOIN Ordered o ON o.ID = s.ID; + + DROP TABLE #Groups; + DROP TABLE #RankMap; + + COMMIT TRAN; + END TRY + BEGIN CATCH + IF @@TRANCOUNT > 0 ROLLBACK TRAN; + THROW; + END CATCH +END; diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql new file mode 100644 index 0000000000..176422b549 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql @@ -0,0 +1,59 @@ +CREATE OR ALTER PROCEDURE usp_MoveCompetencyInSelfAssessment + @SelfAssessmentID INT, + @CompetencyID INT, + @Direction NVARCHAR(10) +AS +BEGIN + SET NOCOUNT ON; + + DECLARE @GroupID INT, @CurrentOrder INT; + + SELECT + @GroupID = CompetencyGroupID, + @CurrentOrder = Ordering + FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID; + + IF @GroupID IS NULL + BEGIN + -- Can't reorder ungrouped competencies via group-based logic + RETURN; + END + + DECLARE @TargetCompetencyID INT, @TargetOrder INT; + + IF @Direction = 'up' + BEGIN + SELECT TOP 1 + @TargetCompetencyID = CompetencyID, + @TargetOrder = Ordering + FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @SelfAssessmentID + AND CompetencyGroupID = @GroupID + AND Ordering < @CurrentOrder + ORDER BY Ordering DESC; + END + ELSE IF @Direction = 'down' + BEGIN + SELECT TOP 1 + @TargetCompetencyID = CompetencyID, + @TargetOrder = Ordering + FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @SelfAssessmentID + AND CompetencyGroupID = @GroupID + AND Ordering > @CurrentOrder + ORDER BY Ordering ASC; + END + + IF @TargetCompetencyID IS NOT NULL + BEGIN + -- Swap the orderings + UPDATE SelfAssessmentStructure + SET Ordering = @TargetOrder + WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID; + + UPDATE SelfAssessmentStructure + SET Ordering = @CurrentOrder + WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @TargetCompetencyID; + END +END diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6437-usp_GetSelfAssessmentReport_Down.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6437-usp_GetSelfAssessmentReport_Down.sql new file mode 100644 index 0000000000..12698cb28f Binary files /dev/null and b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6437-usp_GetSelfAssessmentReport_Down.sql differ diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6437-usp_GetSelfAssessmentReport_Up.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6437-usp_GetSelfAssessmentReport_Up.sql new file mode 100644 index 0000000000..2381fce364 Binary files /dev/null and b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6437-usp_GetSelfAssessmentReport_Up.sql differ diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql new file mode 100644 index 0000000000..554164472c --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql @@ -0,0 +1,56 @@ +/****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +-- ============================================= +-- Author: +-- Modified date: 02/06/2023 +-- Description: Return the admin user details +-- ============================================= + +ALTER VIEW [dbo].[AdminUsers] AS +SELECT dbo.AdminAccounts.ID AS AdminID, + null AS Login, + dbo.Users.PasswordHash AS Password, + dbo.AdminAccounts.CentreID, + dbo.Centres.CentreName, + dbo.AdminAccounts.IsCentreAdmin AS CentreAdmin, + 0 AS ConfigAdmin, + dbo.AdminAccounts.IsReportsViewer AS SummaryReports, + dbo.AdminAccounts.IsSuperAdmin AS UserAdmin, + dbo.Users.FirstName AS Forename, + dbo.Users.LastName AS Surname, + dbo.Users.PrimaryEmail AS Email, + dbo.AdminAccounts.IsCentreManager, + 1 AS Approved, + 0 AS PasswordReminder, + '' AS EITSProfile, + null AS PasswordReminderHash, + null AS PasswordReminderDate, + dbo.AdminAccounts.Active, + dbo.AdminAccounts.IsContentManager AS ContentManager, + dbo.AdminAccounts.PublishToAll, + dbo.AdminAccounts.ImportOnly, + dbo.Users.TermsAgreed AS TCAgreed, + dbo.AdminAccounts.IsContentCreator AS ContentCreator, + dbo.Users.FailedLoginCount, + dbo.Users.ProfileImage, + dbo.AdminAccounts.IsSupervisor AS Supervisor, + dbo.AdminAccounts.IsTrainer AS Trainer, + ISNULL(dbo.AdminAccounts.CategoryID, 0) AS CategoryID, + null AS SkypeHandle, + 0 AS PublicSkypeLink, + dbo.AdminAccounts.IsFrameworkDeveloper, + dbo.Users.ResetPasswordID, + dbo.AdminAccounts.IsFrameworkContributor, + dbo.AdminAccounts.IsWorkforceManager, + dbo.AdminAccounts.IsWorkforceContributor, + dbo.AdminAccounts.IsLocalWorkforceManager, + dbo.AdminAccounts.IsNominatedSupervisor AS NominatedSupervisor +FROM dbo.Users + INNER JOIN dbo.AdminAccounts ON dbo.Users.ID = dbo.AdminAccounts.UserID + INNER JOIN dbo.Centres ON dbo.AdminAccounts.CentreID = dbo.Centres.CentreID +GO diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql new file mode 100644 index 0000000000..e0b8e7fbe5 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql @@ -0,0 +1,57 @@ +/****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +-- ============================================= +-- Author: +-- Modified date: 02/06/2023 +-- Description: Return the admin user details +-- ============================================= + +ALTER VIEW [dbo].[AdminUsers] AS +SELECT dbo.AdminAccounts.ID AS AdminID, + dbo.AdminAccounts.UserID AS AdminUserID, + null AS Login, + dbo.Users.PasswordHash AS Password, + dbo.AdminAccounts.CentreID, + dbo.Centres.CentreName, + dbo.AdminAccounts.IsCentreAdmin AS CentreAdmin, + 0 AS ConfigAdmin, + dbo.AdminAccounts.IsReportsViewer AS SummaryReports, + dbo.AdminAccounts.IsSuperAdmin AS UserAdmin, + dbo.Users.FirstName AS Forename, + dbo.Users.LastName AS Surname, + dbo.Users.PrimaryEmail AS Email, + dbo.AdminAccounts.IsCentreManager, + 1 AS Approved, + 0 AS PasswordReminder, + '' AS EITSProfile, + null AS PasswordReminderHash, + null AS PasswordReminderDate, + dbo.AdminAccounts.Active, + dbo.AdminAccounts.IsContentManager AS ContentManager, + dbo.AdminAccounts.PublishToAll, + dbo.AdminAccounts.ImportOnly, + dbo.Users.TermsAgreed AS TCAgreed, + dbo.AdminAccounts.IsContentCreator AS ContentCreator, + dbo.Users.FailedLoginCount, + dbo.Users.ProfileImage, + dbo.AdminAccounts.IsSupervisor AS Supervisor, + dbo.AdminAccounts.IsTrainer AS Trainer, + ISNULL(dbo.AdminAccounts.CategoryID, 0) AS CategoryID, + null AS SkypeHandle, + 0 AS PublicSkypeLink, + dbo.AdminAccounts.IsFrameworkDeveloper, + dbo.Users.ResetPasswordID, + dbo.AdminAccounts.IsFrameworkContributor, + dbo.AdminAccounts.IsWorkforceManager, + dbo.AdminAccounts.IsWorkforceContributor, + dbo.AdminAccounts.IsLocalWorkforceManager, + dbo.AdminAccounts.IsNominatedSupervisor AS NominatedSupervisor +FROM dbo.Users + INNER JOIN dbo.AdminAccounts ON dbo.Users.ID = dbo.AdminAccounts.UserID + INNER JOIN dbo.Centres ON dbo.AdminAccounts.CentreID = dbo.Centres.CentreID +GO diff --git a/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs b/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs index 6616954ef6..2faf24ae7e 100644 --- a/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs +++ b/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs @@ -88,7 +88,7 @@ IEnumerable resourceReferenceIds public async Task GetCatalogues() { - var response = await GetStringAsync("/Catalogues"); + var response = await GetStringAsync("/Catalogue"); var result = JsonConvert.DeserializeObject(response); return result; } diff --git a/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs new file mode 100644 index 0000000000..c285a4c28d --- /dev/null +++ b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs @@ -0,0 +1,1788 @@ +namespace DigitalLearningSolutions.Data.DataServices +{ + using Dapper; + using DigitalLearningSolutions.Data.Extensions; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using Microsoft.Extensions.Logging; + using System; + using System.Collections.Generic; + using System.Data; + using System.Linq; + public interface ICompetencyAssessmentDataService + { + //GET DATA + IEnumerable GetAllCompetencyAssessments(int adminId); + + IEnumerable GetCompetencyAssessmentsForAdminId(int adminId); + + CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId); + + CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId); + CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId); + IEnumerable GetNRPProfessionalGroups(); + IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID); + IEnumerable GetNRPRoles(int? nRPSubGroupID); + + CompetencyAssessmentTaskStatus GetOrInsertAndReturnAssessmentTaskStatus(int assessmentId, bool frameworkBased); + + int[] GetLinkedFrameworkIds(int assessmentId); + + int? GetPrimaryLinkedFrameworkId(int assessmentId); + + int GetCompetencyCountByFrameworkId(int assessmentId, int frameworkId); + + IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId); + IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId); + int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId); + CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId); + int? GetSelfAssessmentStructure(int competencyAssessmentId); + IEnumerable GetCompetencyWithAssessmentQuestionRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId); + IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId); + SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId); + SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId); + //UPDATE DATA + bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName); + + bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId); + bool UpdateCompetencyAssessmentBranding( + int competencyAssessmentId, + int adminId, + int brandId, + int categoryId + ); + bool UpdateCompetencyAssessmentVocabulary(int competencyAssessmentId, int adminId, string vocabulary); + bool UpdateCompetencyAssessmentDescription(int competencyAssessmentId, int adminId, string competencyAssessmentDescription); + bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus); + bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus); + bool UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus); + bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus); + bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId); + bool UpdateSelectCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateCompetencyAssessmentOptions( + bool includeLearnerDeclarationPrompt, + bool includesSignposting, + bool linearNavigation, + bool useDescriptionExpanders, + string? questionLabelText, + string? reviewerCommentsLabelText, + int competencyAssessmentId, int adminId); + bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus); + void MoveCompetencyInSelfAssessment(int competencyAssessmentId, + int competencyId, + string direction + ); + void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, + int groupId, + string direction + ); + bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus, + bool workingGroupStatus, bool AllframeworkCompetenciesStatus); + bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus); + void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId); + bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds); + void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies); + void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string manageOptionalCompetenciesPrompt); + bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId); + void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters); + int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId); + bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked); + bool UpdateSelfAssessments(int competencyAssessmentId, + int? signoff, + int? confirm, + int? supervisorDeclarationValue, + string? supervisorCustomText, + int? leanerDeclarationValue, + string? leanerCustomText + ); + void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId); + void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub); + void ArchiveSelfAssessmentReviewRequest(int reviewId); + void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId); + void UpdateReviewRequestedDate(int reviewId); + bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus); + //INSERT DATA + int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName); + bool InsertSelfAssessmentFramework(int adminId, int selfAssessmentId, int frameworkId); + bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId); + bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId); + bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId); + int InsertAssessmentQuestionRoleRequirementForSelfAssessment(int assessmentId, int assessmentQuestionId, int levelValue, int? levelRAG); + int InsertCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, int levelValue, int? levelRAG); + void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId); + void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required); + int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId); + int InsertCompetencySelfAssessmentReview(int reviewId); + + //DELETE DATA + bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId); + bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId); + bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId); + IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId); + int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID); + void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id); + CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId); + bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId); + bool DeleteCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int? competencyId, int assessmentQuestionId, int levelValue); + } + + public class CompetencyAssessmentDataService : ICompetencyAssessmentDataService + { + private const string SelfAssessmentBaseFields = @"sa.ID, sa.Name AS CompetencyAssessmentName, sa.Description, sa.BrandID, + sa.ParentSelfAssessmentID, + sa.[National], sa.[Public], sa.CreatedByAdminID AS OwnerAdminID, + sa.NRPProfessionalGroupID, + sa.NRPSubGroupID, + sa.NRPRoleID, + sa.PublishStatusID, sa.Vocabulary, CASE WHEN sa.CreatedByAdminID = @adminId THEN 3 WHEN sac.CanModify = 1 THEN 2 WHEN sac.CanModify = 0 THEN 1 ELSE 0 END AS UserRole, + sa.EnforceRoleRequirementsForSignOff, sa.IncludeRequirementsFilters, + sa.MinimumOptionalCompetencies, + sa.ManageOptionalCompetenciesPrompt, + sa.IncludeLearnerDeclarationPrompt, sa.IncludesSignposting, sa.LinearNavigation, sa.UseDescriptionExpanders, sa.QuestionLabel, sa.ReviewerCommentsLabel, + sa.SupervisorSelfAssessmentReview, sa.SupervisorResultsReview, sar.ID AS SelfAssessmentReviewID, sa.SignOffSupervisorStatement, sa.SignOffRequestorStatement, + sar.SelfAssessmentCommentID"; + + private const string SelfAssessmentFields = + @", sa.CategoryID, sa.CreatedDate, + (SELECT BrandName + FROM Brands + WHERE (BrandID = sa.BrandID)) AS Brand, + (SELECT CategoryName + FROM CourseCategories + WHERE (CourseCategoryID = sa.CategoryID)) AS Category, + (SELECT [Name] + FROM SelfAssessments AS sa2 + WHERE (ID = sa.ParentSelfAssessmentID)) AS ParentSelfAssessment, + (SELECT Forename + ' ' + Surname + (CASE WHEN Active = 1 THEN '' ELSE ' (Inactive)' END) AS Expr1 + FROM AdminUsers + WHERE (AdminID = sa.CreatedByAdminID)) AS Owner, + sa.Archived, + sa.LastEdit, + STUFF(( + SELECT + ', ' + f.FrameworkName + FROM + SelfAssessmentFrameworks saf2 + INNER JOIN Frameworks f ON f.ID = saf2.FrameworkId + WHERE + saf2.SelfAssessmentId = sa.ID AND saf2.RemovedDate IS NULL + FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '' + ) AS LinkedFrameworks, + (SELECT ProfessionalGroup + FROM NRPProfessionalGroups + WHERE (ID = sa.NRPProfessionalGroupID)) AS NRPProfessionalGroup, + (SELECT SubGroup + FROM NRPSubGroups + WHERE (ID = sa.NRPSubGroupID)) AS NRPSubGroup, + (SELECT RoleProfile + FROM NRPRoles + WHERE (ID = sa.NRPRoleID)) AS NRPRole, sar.ID AS SelfAssessmentReviewID"; + + private const string SelfAssessmentBaseTables = + @"SelfAssessments AS sa LEFT OUTER JOIN + SelfAssessmentCollaborators AS sac ON sac.SelfAssessmentID = sa.ID AND sac.AdminID = @adminId"; + + private const string SelfAssessmentTables = + @" LEFT OUTER JOIN + SelfAssessmentReviews AS sar ON sac.ID = sar.SelfAssessmentCollaboratorID AND sar.Archived IS NULL AND sar.ReviewComplete IS NULL"; + + private readonly IDbConnection connection; + private readonly ILogger logger; + + public CompetencyAssessmentDataService(IDbConnection connection, ILogger logger) + { + this.connection = connection; + this.logger = logger; + } + + public IEnumerable GetAllCompetencyAssessments(int adminId) + { + return connection.Query( + $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields} + FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}", + new { adminId } + ); + } + + public IEnumerable GetCompetencyAssessmentsForAdminId(int adminId) + { + return connection.Query( + $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields} + FROM {SelfAssessmentBaseTables} {SelfAssessmentTables} + WHERE (sa.CreatedByAdminID = @adminId) OR + (@adminId IN + (SELECT AdminID + FROM SelfAssessmentCollaborators + WHERE (SelfAssessmentID = sa.ID)))", + new { adminId } + ); + } + + public CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId) + { + return connection.Query( + $@"SELECT {SelfAssessmentBaseFields} + FROM {SelfAssessmentBaseTables} {SelfAssessmentTables} + WHERE (sa.ID = @competencyAssessmentId)", + new { competencyAssessmentId, adminId } + ).FirstOrDefault(); + } + + public int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName) + { + if ((competencyAssessmentName.Length == 0) | (adminId < 1)) + { + logger.LogWarning( + $"Not inserting competency assessmente as it failed server side validation. AdminId: {adminId}, competencyAssessmentName: {competencyAssessmentName}" + ); + return -1; + } + var result = connection.ExecuteScalar( + @"SELECT COUNT(*) FROM SelfAssessments WHERE [Name] = @competencyAssessmentName", + new { competencyAssessmentName } + ); + int existingSelfAssessments = Convert.ToInt32(result); + if (existingSelfAssessments > 0) + { + return -1; + } + var assessmentId = connection.QuerySingle( + @"INSERT INTO SelfAssessments ([Name], CreatedByCentreID, CreatedByAdminID) + OUTPUT INSERTED.Id + VALUES (@competencyAssessmentName, @centreId, @adminId)" + , + new { competencyAssessmentName, centreId, adminId } + ); + return assessmentId; + } + public bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName) + { + if ((competencyAssessmentName.Length == 0) | (adminId < 1) | (competencyAssessmentId < 1)) + { + logger.LogWarning( + $"Not updating role profile name as it failed server side validation. AdminId: {adminId}, competencyAssessmentName: {competencyAssessmentName}, competencyAssessmentId: {competencyAssessmentId}" + ); + return false; + } + var result = connection.ExecuteScalar( + @"SELECT COUNT(*) FROM SelfAssessments WHERE [Name] = @competencyAssessmentName AND ID <> @competencyAssessmentId", + new { competencyAssessmentName, competencyAssessmentId } + ); + int existingSelfAssessments = Convert.ToInt32(result); + if (existingSelfAssessments > 0) + { + return false; + } + + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessments SET [Name] = @competencyAssessmentName, UpdatedByAdminID = @adminId + WHERE ID = @competencyAssessmentId", + new { competencyAssessmentName, adminId, competencyAssessmentId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating role profile name as db update failed. " + + $"SelfAssessmentName: {competencyAssessmentName}, admin id: {adminId}, competencyAssessmentId: {competencyAssessmentId}" + ); + return false; + } + + return true; + } + + public IEnumerable GetNRPProfessionalGroups() + { + return connection.Query( + @"SELECT ID, ProfessionalGroup, Active + FROM NRPProfessionalGroups + WHERE (Active = 1) + ORDER BY ProfessionalGroup" + ); + } + + public CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId) + { + return connection.Query( + $@"SELECT {SelfAssessmentBaseFields} + FROM {SelfAssessmentBaseTables} {SelfAssessmentTables} + WHERE (sa.Name = @competencyAssessmentName)", + new { competencyAssessmentName, adminId } + ).FirstOrDefault(); + } + + public bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId) + { + var result = connection.ExecuteScalar( + @"SELECT COUNT(*) FROM SelfAssessments WHERE ID = @competencyAssessmentId AND NRPProfessionalGroupID = @professionalGroupId AND NRPSubGroupID = @subGroupId AND NRPRoleID = @roleId", + new { competencyAssessmentId, professionalGroupId, subGroupId, roleId } + ); + int sameCount = Convert.ToInt32(result); + if (sameCount > 0) + { + //same so don't update: + return false; + } + + //needs updating: + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessments SET NRPProfessionalGroupID = @professionalGroupId, NRPSubGroupID = @subGroupId, NRPRoleID = @roleId, UpdatedByAdminID = @adminId + WHERE ID = @competencyAssessmentId", + new { adminId, competencyAssessmentId, professionalGroupId, subGroupId, roleId } + ); + if (numberOfAffectedRows > 0) + { + return true; + } + + return false; + } + public bool UpdateCompetencyAssessmentBranding( + int competencyAssessmentId, + int adminId, + int brandId, + int categoryId + ) + { + if ((competencyAssessmentId < 1) | (brandId < 1) | (categoryId < 1) | (adminId < 1)) + { + logger.LogWarning( + $"Not updating competency assessment as it failed server side validation. competencyAssessmentId: {competencyAssessmentId}, brandId: {brandId}, categoryId: {categoryId}, AdminId: {adminId}" + ); + return false; + } + + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessments SET BrandID = @brandId, CategoryID = @categoryId, UpdatedByAdminID = @adminId + WHERE ID = @competencyAssessmentId", + new { brandId, categoryId, adminId, competencyAssessmentId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating competency assessment branding as db update failed. " + + $"frameworkId: {competencyAssessmentId}, brandId: {brandId}, categoryId: {categoryId}, AdminId: {adminId}" + ); + return false; + } + + return true; + } + + public bool UpdateCompetencyAssessmentDescription(int competencyAssessmentId, int adminId, string competencyAssessmentDescription) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessments SET Description = @competencyAssessmentDescription, UpdatedByAdminID = @adminId + WHERE ID = @competencyAssessmentId", + new { adminId, competencyAssessmentId, competencyAssessmentDescription } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating competency assessment Description as db update failed. " + + $"frameworkId: {competencyAssessmentId}, competencyAssessmentDescription: {competencyAssessmentDescription}, AdminId: {adminId}" + ); + return false; + } + return true; + } + + public bool UpdateCompetencyAssessmentVocabulary(int competencyAssessmentId, int adminId, string vocabulary) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessments SET Vocabulary = @vocabulary, UpdatedByAdminID = @adminId + WHERE ID = @competencyAssessmentId", + new { adminId, competencyAssessmentId, vocabulary } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating competency assessment vocabulary as db update failed. " + + $"frameworkId: {competencyAssessmentId}, vocabulary: {vocabulary}, AdminId: {adminId}" + ); + return false; + } + return true; + } + + public bool InsertSelfAssessmentFramework(int adminId, int selfAssessmentId, int frameworkId) + { + bool isPrimary = Convert.ToInt32(connection.ExecuteScalar( + @"SELECT Count(1) FROM SelfAssessmentFrameworks + WHERE SelfAssessmentId = @selfAssessmentId AND IsPrimary = 1", new { selfAssessmentId })) == 0; + + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO SelfAssessmentFrameworks (SelfAssessmentId, FrameworkId, CreatedByAdminId, IsPrimary) + SELECT @selfAssessmentId, @frameworkId, @adminId, @isPrimary + WHERE NOT EXISTS (SELECT 1 FROM SelfAssessmentFrameworks WHERE SelfAssessmentId = @selfAssessmentId AND FrameworkId = @frameworkId)" + , + new { adminId, selfAssessmentId, frameworkId, isPrimary } + ); + if (numberOfAffectedRows < 1) + { + numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentFrameworks + SET RemovedDate = NULL, RemovedByAdminId = NULL, AmendedByAdminId = @adminId, IsPrimary = 0 + WHERE SelfAssessmentId = @selfAssessmentId AND FrameworkId = @frameworkId" + , + new { adminId, selfAssessmentId, frameworkId } + ); + } + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not inserting SelfAssessmentFrameworks record as db insert failed. " + + $"selfAssessmentId: {selfAssessmentId}, frameworkId: {frameworkId}, AdminId: {adminId}" + ); + return false; + } + + return true; + } + + public CompetencyAssessmentTaskStatus GetOrInsertAndReturnAssessmentTaskStatus(int assessmentId, bool frameworkBased) + { + bool? frameworkItemBool = frameworkBased ? false : null; + connection.Execute( + @"INSERT INTO SelfAssessmentTaskStatus (SelfAssessmentId, IntroductoryTextTaskStatus, BrandingTaskStatus, VocabularyTaskStatus, FrameworkLinksTaskStatus) + SELECT @assessmentId, @frameworkItemBool, @frameworkItemBool, @frameworkItemBool, @frameworkItemBool + WHERE NOT EXISTS (SELECT 1 FROM SelfAssessmentTaskStatus WHERE SelfAssessmentId = @assessmentId)", new { assessmentId, frameworkItemBool }); + return connection.Query( + $@"SELECT * + FROM SelfAssessmentTaskStatus + WHERE (SelfAssessmentId = @assessmentId)", + new { assessmentId } + ).Single(); + + } + public bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET IntroductoryTextTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId", + new { assessmentId, taskStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating IntroductoryTextTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId) + { + return connection.Query( + $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields} + FROM {SelfAssessmentBaseTables} {SelfAssessmentTables} + WHERE (sa.ID = @competencyAssessmentId)", + new { competencyAssessmentId, adminId } + ).FirstOrDefault(); + } + + public bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET BrandingTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId", + new { assessmentId, taskStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating BrandingTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public bool UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET VocabularyTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId", + new { assessmentId, taskStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating VocabularyTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID) + { + return connection.Query( + @"SELECT ID, SubGroup, Active + FROM NRPSubGroups + WHERE (Active = 1) AND (NRPProfessionalGroupID = @nRPProfessionalGroupID) + ORDER BY SubGroup", new { nRPProfessionalGroupID } + ); + } + + public IEnumerable GetNRPRoles(int? nRPSubGroupID) + { + return connection.Query( + @"SELECT ID, RoleProfile AS ProfileName, Active + FROM NRPRoles + WHERE (Active = 1) AND (NRPSubGroupID = @nRPSubGroupID) + ORDER BY RoleProfile", new { nRPSubGroupID } + ); + } + + public bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET NationalRoleProfileTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId", + new { assessmentId, taskStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating NationalRoleProfileTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public int[] GetLinkedFrameworkIds(int assessmentId) + { + return [.. connection.Query( + @"SELECT FrameworkId + FROM SelfAssessmentFrameworks + WHERE (SelfAssessmentId = @assessmentId) AND (RemovedDate IS NULL) AND (IsPrimary = 0) + ORDER BY ID", + new { assessmentId } + )]; + } + + public bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentFrameworks SET RemovedDate = @removedDate, RemovedByAdminId = @adminId + WHERE SelfAssessmentId = @assessmentId AND FrameworkId = @frameworkId", + new { removedDate = DateTime.Now, assessmentId, frameworkId, adminId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating SelfAssessmentFrameworks as db update failed. " + + $"assessmentId: {assessmentId}, frameworkId: {frameworkId}, adminId: {adminId}" + ); + return false; + } + return true; + } + + public int? GetPrimaryLinkedFrameworkId(int assessmentId) + { + return connection.QuerySingleOrDefault( + @"SELECT TOP(1) FrameworkId + FROM SelfAssessmentFrameworks + WHERE (SelfAssessmentId = @assessmentId) AND (RemovedDate IS NULL) AND (IsPrimary = 1) + ORDER BY ID DESC", + new { assessmentId } + ); + } + + public bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET FrameworkLinksTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR FrameworkLinksTaskStatus = @previousStatus)", + new { assessmentId, taskStatus, previousStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating FrameworkLinksTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public int GetCompetencyCountByFrameworkId(int assessmentId, int frameworkId) + { + return connection.ExecuteScalar( + @"SELECT COUNT(sas.CompetencyID) AS Competencies + FROM SelfAssessmentStructure AS sas INNER JOIN + FrameworkCompetencies AS fc ON sas.CompetencyID = fc.CompetencyID INNER JOIN + SelfAssessmentFrameworks AS saf ON fc.FrameworkID = saf.FrameworkId AND sas.SelfAssessmentID = saf.SelfAssessmentId + WHERE (saf.SelfAssessmentId = @assessmentId) AND (saf.FrameworkId = @frameworkId)", + new { assessmentId, frameworkId } + ); + } + + public bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId) + { + var numberOfAffectedRows = connection.Execute( + @"DELETE FROM SelfAssessmentStructure + FROM SelfAssessmentStructure INNER JOIN + FrameworkCompetencies AS fc ON SelfAssessmentStructure.CompetencyID = fc.CompetencyID INNER JOIN + SelfAssessmentFrameworks AS saf ON fc.FrameworkID = saf.FrameworkId AND SelfAssessmentStructure.SelfAssessmentID = saf.SelfAssessmentId + WHERE (saf.SelfAssessmentId = @competencyAssessmentId) AND (saf.FrameworkId = @frameworkId)", + new { competencyAssessmentId, frameworkId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not removing competencies linked to source framework as db update failed. " + + $"assessmentId: {competencyAssessmentId}, taskStatus: {frameworkId}" + ); + return false; + } + return true; + } + + public bool UpdateSelectCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET SelectCompetenciesTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR SelectCompetenciesTaskStatus = @previousStatus)", + new { assessmentId, taskStatus, previousStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating SelectCompetenciesTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + public bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET OptionalCompetenciesTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR OptionalCompetenciesTaskStatus = @previousStatus)", + new { assessmentId, taskStatus, previousStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating OptionalCompetenciesTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + public bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET RoleRequirementsTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR RoleRequirementsTaskStatus = @previousStatus)", + new { assessmentId, taskStatus, previousStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating RoleRequirementsTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + public bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET WorkingGroupTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR WorkingGroupTaskStatus = @previousStatus)", + new { assessmentId, taskStatus, previousStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating WorkingGroupTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId) + { + return connection.Query( + @"SELECT sas.ID AS StructureId, sas.CompetencyID, f.ID AS FrameworkId, f.FrameworkName, cg.ID AS GroupId, cg.Name AS GroupName, c.Name AS CompetencyName, c.Description AS CompetencyDescription, sas.Optional, sas.GroupOptionalCompetencies + FROM SelfAssessmentStructure AS sas INNER JOIN + Competencies AS c ON sas.CompetencyID = c.ID LEFT JOIN + CompetencyGroups AS cg ON sas.CompetencyGroupID = cg.ID INNER JOIN + FrameworkCompetencies ON c.ID = FrameworkCompetencies.CompetencyID INNER JOIN + Frameworks AS f ON FrameworkCompetencies.FrameworkID = f.ID INNER JOIN + SelfAssessmentFrameworks ON f.ID = SelfAssessmentFrameworks.FrameworkId AND sas.SelfAssessmentID = SelfAssessmentFrameworks.SelfAssessmentId + WHERE (sas.SelfAssessmentID = @competencyAssessmentId) + ORDER BY sas.Ordering", new { competencyAssessmentId } + ); + } + + public IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId) + { + return connection.Query( + @"SELECT f.ID, + FrameworkName, + f.OwnerAdminID, + f.BrandID, + f.CategoryID, + f.TopicID, + f.CreatedDate, + f.PublishStatusID, + f.UpdatedByAdminID, + saf.IsPrimary + FROM SelfAssessmentFrameworks saf INNER JOIN + Frameworks AS f ON saf.FrameworkId = f.ID + WHERE (saf.SelfAssessmentId = @competencyAssessmentId) AND (saf.RemovedDate IS NULL) + ORDER BY saf.ID", new { competencyAssessmentId } + ); + } + + public int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId) + { + return [.. connection.Query( + @"SELECT sas.CompetencyID + FROM SelfAssessmentStructure AS sas INNER JOIN + FrameworkCompetencies AS fc ON sas.CompetencyID = fc.CompetencyID + WHERE (sas.SelfAssessmentID = @competencyAssessmentId) AND (fc.FrameworkID = @frameworkId) + ORDER BY fc.Ordering", + new { competencyAssessmentId, frameworkId} + )]; + } + + public bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId) + { + + var currentMaxOrdering = connection.ExecuteScalar( + @"SELECT ISNULL(MAX(Ordering), 0) FROM SelfAssessmentStructure WHERE SelfAssessmentID = @competencyAssessmentId", + new { competencyAssessmentId } + ); + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO SelfAssessmentStructure (SelfAssessmentID, CompetencyID, Ordering, CompetencyGroupID) + SELECT + @competencyAssessmentId, + FC.CompetencyID, + ROW_NUMBER() OVER (ORDER BY FCG.Ordering, FC.Ordering) + @currentMaxOrdering, + FCG.CompetencyGroupID + FROM FrameworkCompetencies AS FC + LEFT JOIN FrameworkCompetencyGroups AS FCG ON FC.FrameworkCompetencyGroupID = FCG.ID + WHERE FC.FrameworkID = @frameworkId + AND FC.CompetencyID IN @selectedCompetencyIds AND FC.CompetencyID NOT IN (SELECT CompetencyID FROM SelfAssessmentStructure WHERE SelfAssessmentID = @competencyAssessmentId)", + new { selectedCompetencyIds, frameworkId, competencyAssessmentId, currentMaxOrdering } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not inserting competencies into assessment as db update failed. " + + $"assessmentId: {competencyAssessmentId}, frameworkId: {frameworkId}, selectedCompetencyIds: {selectedCompetencyIds}" + ); + return false; + } + return true; + } + public bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId) + { + var numberOfAffectedRows = connection.Execute( + @"DELETE FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @competencyAssessmentId AND CompetencyID = @competencyId", + new { competencyAssessmentId, competencyId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not removing competency from assessment as db update failed. " + + $"assessmentId: {competencyAssessmentId}, competencyId: {competencyId}" + ); + return false; + } + return true; + } + + public bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId) + { + var numberOfAffectedRows = connection.Execute( + @"DELETE FROM SelfAssessmentStructure + WHERE SelfAssessmentID = @competencyAssessmentId AND CompetencyGroupId = @competencyGroupId", + new { competencyAssessmentId, competencyGroupId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not removing competency from assessment as db update failed. " + + $"assessmentId: {competencyAssessmentId}, competencyGroupId: {competencyGroupId}" + ); + return false; + } + return true; + } + + public void MoveCompetencyInSelfAssessment(int competencyAssessmentId, int competencyId, string direction) + { + connection.Execute( + "usp_MoveCompetencyInSelfAssessment", + new { SelfAssessmentID = competencyAssessmentId, CompetencyID = competencyId, Direction = direction }, + commandType: CommandType.StoredProcedure + ); + + } + + public void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, int groupId, string direction) + { + connection.Execute( + "usp_MoveCompetencyGroupInSelfAssessment", + new { SelfAssessmentID = competencyAssessmentId, GroupID = groupId, Direction = direction }, + commandType: CommandType.StoredProcedure + ); + } + + public bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus, + bool workingGroupStatus, bool AllframeworkCompetenciesStatus) + { + var numberOfAffectedRows = connection.Execute( + @"IF EXISTS (SELECT 1 FROM SelfAssessmentTaskStatus WHERE SelfAssessmentId = @id) + BEGIN + UPDATE SelfAssessmentTaskStatus + SET IntroductoryTextTaskStatus = + CASE WHEN @descriptionStatus = 1 AND (IntroductoryTextTaskStatus IS NULL OR IntroductoryTextTaskStatus <> 1) + THEN 0 ELSE IntroductoryTextTaskStatus END, + BrandingTaskStatus = + CASE WHEN @providerandCategoryStatus = 1 AND (BrandingTaskStatus IS NULL OR BrandingTaskStatus <> 1) + THEN 0 ELSE BrandingTaskStatus END, + VocabularyTaskStatus = + CASE WHEN @vocabularyStatus = 1 AND (VocabularyTaskStatus IS NULL OR VocabularyTaskStatus <> 1) + THEN 0 ELSE VocabularyTaskStatus END, + WorkingGroupTaskStatus = + CASE WHEN @workingGroupStatus = 1 AND (WorkingGroupTaskStatus IS NULL OR WorkingGroupTaskStatus <> 1) + THEN 0 ELSE WorkingGroupTaskStatus END, + FrameworkLinksTaskStatus = + CASE WHEN @AllframeworkCompetenciesStatus = 1 AND (FrameworkLinksTaskStatus IS NULL OR FrameworkLinksTaskStatus <> 1) + THEN 0 ELSE FrameworkLinksTaskStatus END, + SelectCompetenciesTaskStatus = + CASE WHEN @AllframeworkCompetenciesStatus = 1 AND (SelectCompetenciesTaskStatus IS NULL OR SelectCompetenciesTaskStatus <> 1) + THEN 0 ELSE SelectCompetenciesTaskStatus END + WHERE SelfAssessmentId = @id; + END + ELSE + BEGIN + INSERT INTO SelfAssessmentTaskStatus + (SelfAssessmentId, IntroductoryTextTaskStatus, BrandingTaskStatus, VocabularyTaskStatus, WorkingGroupTaskStatus, + FrameworkLinksTaskStatus,SelectCompetenciesTaskStatus) + VALUES + ( + @id, + CASE WHEN @descriptionStatus = 1 THEN 0 ELSE NULL END, + CASE WHEN @providerandCategoryStatus = 1 THEN 0 ELSE NULL END, + CASE WHEN @vocabularyStatus = 1 THEN 0 ELSE NULL END, + CASE WHEN @workingGroupStatus = 1 THEN 0 ELSE NULL END, + CASE WHEN @AllframeworkCompetenciesStatus = 1 THEN 0 ELSE NULL END, + CASE WHEN @AllframeworkCompetenciesStatus = 1 THEN 0 ELSE NULL END + ); + END", + new { id, descriptionStatus, providerandCategoryStatus, vocabularyStatus, workingGroupStatus, AllframeworkCompetenciesStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating SelfAssessmentTaskStatus as db update failed. " + + $"SelfAssessmentId: {id}, IntroductoryTextTaskStatus: {descriptionStatus}, BrandingTaskStatus: {providerandCategoryStatus}, " + + $"VocabularyTaskStatus: {vocabularyStatus}, WorkingGroupTaskStatus: {workingGroupStatus}, FrameworkLinksTaskStatus: {AllframeworkCompetenciesStatus}" + ); + return false; + } + return true; + } + + public CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId) + { + return connection.QueryFirstOrDefault( + @"SELECT s.ID, s.Name AS CompetencyAssessmentName, sts.IntroductoryTextTaskStatus AS DescriptionStatus, sts.BrandingTaskStatus AS ProviderandCategoryStatus, + sts.VocabularyTaskStatus AS VocabularyStatus, sts.WorkingGroupTaskStatus AS WorkingGroupStatus, sts.FrameworkLinksTaskStatus AS AllframeworkCompetenciesStatus + FROM SelfAssessments s INNER JOIN + SelfAssessmentTaskStatus sts ON s.ID = sts.SelfAssessmentId + WHERE s.ID = @competencyAssessmentId", + new { competencyAssessmentId } + ); + + } + + public void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId) + { + + var numberOfAffectedRows = connection.Execute( + @"UPDATE s + SET + [Description] = CASE + WHEN sts.IntroductoryTextTaskStatus IS NULL THEN NULL + ELSE COALESCE(F.[Description], 'No description provided') + END, + BrandID = CASE + WHEN sts.BrandingTaskStatus IS NULL THEN s.BrandID + ELSE F.BrandID + END, + CategoryID = CASE + WHEN sts.BrandingTaskStatus IS NULL THEN s.CategoryID + ELSE F.CategoryID + END, + Vocabulary = CASE + WHEN sts.VocabularyTaskStatus IS NULL THEN NULL + ELSE F.FrameworkConfig + END + FROM SelfAssessments s + INNER JOIN Frameworks F ON F.ID = @frameworkId + INNER JOIN AdminUsers AU ON F.OwnerAdminID = AU.AdminID + INNER JOIN SelfAssessmentTaskStatus sts ON s.ID = sts.SelfAssessmentId + WHERE s.ID = @selfAssessmentId;", + new { selfAssessmentId, frameworkId } + ); + } + public bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId) + { + + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO SelfAssessmentStructure (SelfAssessmentID, CompetencyID, Ordering, CompetencyGroupID) + SELECT s.ID, FC.CompetencyID, ROW_NUMBER() OVER( ORDER BY FCG.Ordering, FC.Ordering ), FCG.CompetencyGroupID + FROM FrameworkCompetencies AS FC + INNER JOIN FrameworkCompetencyGroups AS FCG ON FC.FrameworkCompetencyGroupID = FCG.ID INNER JOIN + SelfAssessments s ON s.id = @selfAssessmentId + WHERE FC.FrameworkID = @frameworkId + AND FC.CompetencyID NOT IN (SELECT CompetencyID FROM SelfAssessmentStructure WHERE SelfAssessmentID = @selfAssessmentId)" + , + new { selfAssessmentId, frameworkId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not inserting SelfAssessmentStructure record as db insert failed. " + + $"selfAssessmentId: {selfAssessmentId}, frameworkId: {frameworkId}" + ); + return false; + } + + return true; + } + public bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId) + { + + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO SelfAssessmentStructure (SelfAssessmentID, CompetencyID, Ordering) + SELECT s.ID, FC.CompetencyID, FC.Ordering + FROM FrameworkCompetencies AS fc + INNER JOIN SelfAssessments s ON s.id = @selfAssessmentId + WHERE fc.FrameworkID = @frameworkId + AND fc.FrameworkCompetencyGroupID IS NULL + AND FC.CompetencyID NOT IN (SELECT CompetencyID FROM SelfAssessmentStructure WHERE SelfAssessmentID = @selfAssessmentId)" + , + new { selfAssessmentId, frameworkId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not inserting SelfAssessmentStructure record as db insert failed. " + + $"selfAssessmentId: {selfAssessmentId}, frameworkId: {frameworkId}" + ); + return false; + } + + return true; + } + + public bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId) + { + connection.EnsureOpen(); + using (var transaction = connection.BeginTransaction()) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentFrameworks + SET IsPrimary = 0 + WHERE (SelfAssessmentId = @assessmentId) + AND (RemovedDate IS NULL)", + new { assessmentId }, + transaction: transaction + ); + + var numberOfAffectedRow = connection.Execute( + @"UPDATE SelfAssessmentFrameworks + SET IsPrimary = 1 + WHERE (SelfAssessmentId = @assessmentId) + AND (FrameworkId = @frameworkId) + AND (RemovedDate IS NULL)", + new { assessmentId, frameworkId }, + transaction: transaction + ); + + if ((numberOfAffectedRow < 1) || (numberOfAffectedRows < 1)) + { + logger.LogWarning( + "Not updating SelfAssessmentFrameworks as db update failed. " + + $"assessmentId: {assessmentId}, frameworkId: {frameworkId}" + ); + transaction.Rollback(); + return false; + } + + transaction.Commit(); + return true; + } + } + public int? GetSelfAssessmentStructure(int competencyAssessmentId) + { + return connection.QueryFirstOrDefault( + @"SELECT 1 from dbo.SelfAssessmentStructure where selfassessmentid = @competencyAssessmentId", + new { competencyAssessmentId } + ); + } + public IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId) + { + return connection.Query( + @"SELECT + 0 AS ID, + sa.ID AS SelfAssessmentID, + au.AdminID AS AdminID, + 1 AS CanModify, + au.Email AS UserEmail, + au.Active AS UserActive, + 'Owner' AS CompetencyAssessmentRole, + 0 AS SelfAssessmentReviewID, + 0 AS SignOffRequired + FROM SelfAssessments AS sa + INNER JOIN AdminUsers AS au ON sa.CreatedByAdminID = au.AdminID + WHERE (sa.ID = @competencyAssessmentId) + UNION ALL + SELECT + sc.ID, + sc.SelfAssessmentID, + sc.AdminID, + CanModify, + UserEmail, + au.Active AS UserActive, + CASE WHEN CanModify = 1 THEN 'Contributor' ELSE 'Reviewer' END AS CompetencyAssessmentRole, + sr.ID AS SelfAssessmentReviewID, + sr.SignOffRequired + FROM SelfAssessmentCollaborators sc + INNER JOIN AdminUsers AS au ON sc.AdminID = au.AdminID + AND sc.IsDeleted = 0 + LEFT OUTER JOIN SelfAssessmentReviews sr ON sr.SelfAssessmentCollaboratorID = sc.ID AND sr.Archived IS NULL + WHERE (sc.SelfAssessmentID = @competencyAssessmentId)", + new { competencyAssessmentId } + ); + } + public int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID) + { + if (userEmail is null || userEmail.Length == 0) + { + logger.LogWarning( + $"Not adding collaborator to competency assessment as it failed server side valiidation. competencyAssessmentId: {competencyAssessmentId}, userEmail: {userEmail}, canModify:{canModify}" + ); + return -3; + } + + var existingId = connection.QuerySingle( + @"SELECT COALESCE + ((SELECT ID + FROM SelfAssessmentCollaborators + WHERE (SelfAssessmentID = @competencyAssessmentId) AND (UserEmail = @userEmail) AND (IsDeleted=0)), 0) AS ID", + new { competencyAssessmentId, userEmail } + ); + if (existingId > 0) + { + return -2; + } + + var adminId = (int?)connection.ExecuteScalar( + @"SELECT AdminID FROM AdminUsers WHERE Email = @userEmail AND Active = 1 AND CentreID = @centreID", + new { userEmail, centreID } + ); + if (adminId is null) + { + return -4; + } + + var ownerEmail = (string?)connection.ExecuteScalar(@"SELECT AU.Email FROM SelfAssessments SA + INNER JOIN AdminUsers AU ON AU.AdminID = SA.CreatedByAdminID + WHERE SA.ID = @competencyAssessmentId", new { competencyAssessmentId }); + if (ownerEmail == userEmail) + { + return -5; + } + + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO SelfAssessmentCollaborators (SelfAssessmentID, AdminID, UserEmail, CanModify) + VALUES (@competencyAssessmentId, @adminId, @userEmail, @canModify)", + new { competencyAssessmentId, adminId, userEmail, canModify } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + $"Not inserting framework collaborator as db insert failed. AdminId: {adminId}, userEmail: {userEmail}, competencyAssessmentId: {competencyAssessmentId}, canModify: {canModify}" + ); + return -1; + } + + if (adminId > 0) + { + connection.Execute( + @"UPDATE AdminUsers SET IsWorkforceManager = 1 WHERE AdminId = @adminId AND IsWorkforceManager = 0", + new { adminId } + ); + } + + existingId = connection.QuerySingle( + @"SELECT COALESCE + ((SELECT ID + FROM SelfAssessmentCollaborators + WHERE (SelfAssessmentID = @competencyAssessmentId) AND (UserEmail = @userEmail) AND (IsDeleted=0)), 0) AS AdminID", + new { competencyAssessmentId, adminId, userEmail } + ); + return existingId; + } + + public void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id) + { + var adminId = (int?)connection.ExecuteScalar( + @"SELECT AdminID FROM SelfAssessmentCollaborators WHERE (SelfAssessmentID = @competencyAssessmentId) AND (ID = @id)", + new { competencyAssessmentId, id } + ); + connection.Execute( + @"UPDATE SelfAssessmentCollaborators SET IsDeleted=1 WHERE (SelfAssessmentID = @competencyAssessmentId) AND (ID = @id); + UPDATE AdminUsers SET IsWorkforceManager = 0 WHERE AdminID = @adminId AND AdminID NOT IN (SELECT DISTINCT AdminID FROM SelfAssessmentCollaborators);", + new { competencyAssessmentId, id, adminId } + ); + } + public CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId) + { + return connection.Query( + @"SELECT + sc.SelfAssessmentID, + sc.AdminID, + sc.CanModify, + sc.UserEmail, + au.Active AS UserActive, + CASE WHEN sc.CanModify = 1 THEN 'Contributor' ELSE 'Reviewer' END AS CompetencyAssessmentRole, + sa.[Name] AS CompetencyAssessmentName, + (SELECT Forename + ' ' + Surname + (CASE WHEN Active = 1 THEN '' ELSE ' (Inactive)' END) AS Expr1 FROM AdminUsers AS au1 WHERE (AdminID = @invitedByAdminId)) AS InvitedByName, + (SELECT Email FROM AdminUsers AS au2 WHERE (AdminID = @invitedByAdminId)) AS InvitedByEmail + FROM SelfAssessmentCollaborators AS sc + INNER JOIN SelfAssessments AS sa ON sc.SelfAssessmentID = sa.ID + INNER JOIN AdminUsers AS au ON sc.AdminID = au.AdminID + WHERE (sc.ID = @id) AND (sc.IsDeleted=0)", + new { invitedByAdminId, id } + ).FirstOrDefault(); + } + + public bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId) + { + int hasSignpostedLearning = connection.QueryFirstOrDefault( + @"SELECT count(*) FROM SelfAssessmentStructure sas INNER JOIN + CompetencyLearningResources clr ON sas.CompetencyID = clr.CompetencyID AND + clr.RemovedDate IS NULL + WHERE sas.SelfAssessmentID = @competencyAssessmentId", + new { competencyAssessmentId }); + + return hasSignpostedLearning > 0; + } + public bool UpdateCompetencyAssessmentOptions( + bool includeLearnerDeclarationPrompt, + bool includesSignposting, + bool linearNavigation, + bool useDescriptionExpanders, + string? questionLabelText, + string? reviewerCommentsLabelText, + int competencyAssessmentId, int adminId) + { + if ((adminId < 1) | (competencyAssessmentId < 1)) + { + logger.LogWarning( + $"Not updating role profile name as it failed server side validation. AdminId: {adminId}, competencyAssessmentId: {competencyAssessmentId}" + ); + return false; + } + + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessments SET IncludeLearnerDeclarationPrompt = @includeLearnerDeclarationPrompt, + IncludesSignposting = @includesSignposting, + LinearNavigation = @linearNavigation, + UseDescriptionExpanders = @useDescriptionExpanders, + QuestionLabel = @questionLabelText, + ReviewerCommentsLabel = @reviewerCommentsLabelText, + UpdatedByAdminID = @adminId + WHERE ID = @competencyAssessmentId ", + new + { + includeLearnerDeclarationPrompt, + includesSignposting, + linearNavigation, + useDescriptionExpanders, + questionLabelText, + reviewerCommentsLabelText, + adminId, + competencyAssessmentId + } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating options/labels as db update failed. " + + $"admin id: {adminId}, competencyAssessmentId: {competencyAssessmentId}" + ); + return false; + } + + return true; + } + + public bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET SelfAssessmentOptionsTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId", + new { assessmentId, taskStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating SelfAssessmentOptionsTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds) + { + const string sql = @"UPDATE sas + SET + Optional = + CASE + WHEN sas.CompetencyGroupID IN @GroupIds THEN 1 + WHEN sas.ID IN @SelectedIds THEN 1 + ELSE 0 + END, + GroupOptionalCompetencies = + CASE + WHEN sas.CompetencyGroupID IN @GroupIds THEN 1 + ELSE 0 + END + FROM SelfAssessmentStructure sas + WHERE sas.SelfAssessmentID = @SelfAssessmentId;"; + + var safeGroupIds = (groupIds != null && groupIds.Length > 0) + ? groupIds + : [-1]; + + var safeSelectedIds = (selectedStructureIds != null && selectedStructureIds.Length > 0) + ? selectedStructureIds + : [-1]; + + + var rows = connection.Execute(sql, new + { + SelfAssessmentId = selfAssessmentId, + GroupIds = safeGroupIds, + SelectedIds = safeSelectedIds + }); + + // Returns true if any rows were updated + return rows > 0; + } + + public void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies) + { + connection.Execute( + @"UPDATE SelfAssessments + SET + [MinimumOptionalCompetencies] = @minimumOptionalCompetecies + WHERE id = @selfAssessmentId AND ISNULL(MinimumOptionalCompetencies, 0) <> @minimumOptionalCompetecies;" + , + new { selfAssessmentId, minimumOptionalCompetecies } + ); + } + public void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string? manageOptionalCompetenciesPrompt) + { + connection.Execute( + @"UPDATE SelfAssessments + SET + [ManageOptionalCompetenciesPrompt] = @manageOptionalCompetenciesPrompt + WHERE id = @selfAssessmentId AND ISNULL(ManageOptionalCompetenciesPrompt, '') <> @manageOptionalCompetenciesPrompt;" + , + new { selfAssessmentId, manageOptionalCompetenciesPrompt } + ); + } + + public IEnumerable GetCompetencyWithAssessmentQuestionRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId) + { + return connection.Query( + @"SELECT + sas.CompetencyGroupID, + cg.Name AS GroupName, + sas.CompetencyID, + c.Name AS Competency, + c.Description AS CompetencyDescription, + sas.Optional, + caq.AssessmentQuestionID, + aq.Question, + caq.Required, + aqit.InputTypeName, + aql.LevelValue AS ResponseValue, + aql.LevelLabel AS Response, + caqrr.LevelRAG +FROM SelfAssessmentStructure AS sas +INNER JOIN Competencies AS c + ON sas.CompetencyID = c.ID +INNER JOIN CompetencyAssessmentQuestions AS caq + ON c.ID = caq.CompetencyID +INNER JOIN AssessmentQuestions AS aq + ON caq.AssessmentQuestionID = aq.ID +INNER JOIN AssessmentQuestionInputTypes AS aqit + ON aq.AssessmentQuestionInputTypeID = aqit.ID +LEFT JOIN CompetencyGroups AS cg + ON sas.CompetencyGroupID = cg.ID + +INNER JOIN AssessmentQuestionLevels AS aql + ON aql.AssessmentQuestionID = aq.ID + +LEFT JOIN CompetencyAssessmentQuestionRoleRequirements AS caqrr + ON caqrr.AssessmentQuestionID = aq.ID + AND caqrr.SelfAssessmentID = sas.SelfAssessmentID + AND caqrr.CompetencyID = sas.CompetencyID + AND caqrr.LevelValue = aql.LevelValue + +WHERE sas.SelfAssessmentID = @competencyAssessmentId + AND (@competencyId IS NULL OR sas.CompetencyID = @competencyId) + AND (@assessmentQuestionId IS NULL OR caq.AssessmentQuestionID = @assessmentQuestionId) + +ORDER BY + sas.Ordering, + caq.Ordering, + aql.LevelValue;", + new { competencyAssessmentId, competencyId, assessmentQuestionId } + ); + } + + public bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET RoleRequirementsTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId", + new { assessmentId, taskStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating RoleRequirementsTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + + public void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters) + { + connection.Execute( + @"UPDATE SelfAssessments + SET + [EnforceRoleRequirementsForSignOff] = @enforceRoleRequirementsForSignOff, [IncludeRequirementsFilters] = @includeRequirementsFilters + WHERE id = @assessmentId AND (EnforceRoleRequirementsForSignOff <> @enforceRoleRequirementsForSignOff OR IncludeRequirementsFilters <> @includeRequirementsFilters);" + , + new { assessmentId, enforceRoleRequirementsForSignOff, includeRequirementsFilters } + ); + } + + public int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId) + { + return connection.QuerySingle( + @"SELECT COUNT(1) + FROM SelfAssessmentStructure AS sas INNER JOIN + Competencies AS c ON sas.CompetencyID = c.ID INNER JOIN + CompetencyAssessmentQuestions AS caq ON c.ID = caq.CompetencyID + WHERE (sas.SelfAssessmentID = @competencyAssessmentId) AND (caq.AssessmentQuestionID = @assessmentQuestionId)", + new { competencyAssessmentId, assessmentQuestionId } + ); + } + + public int InsertAssessmentQuestionRoleRequirementForSelfAssessment(int assessmentId, int assessmentQuestionId, int levelValue, int? levelRAG) + { + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO CompetencyAssessmentQuestionRoleRequirements + (SelfAssessmentID, CompetencyID, AssessmentQuestionID, LevelValue, LevelRAG) + SELECT sas.SelfAssessmentID, sas.CompetencyID, caq.AssessmentQuestionID, @levelValue AS LevelValue, @levelRAG AS LevelRAG + FROM SelfAssessmentStructure AS sas INNER JOIN + CompetencyAssessmentQuestions AS caq ON sas.CompetencyID = caq.CompetencyID + WHERE (sas.SelfAssessmentID = @assessmentId) AND (caq.AssessmentQuestionID = @assessmentQuestionId);" + , + new { assessmentId, assessmentQuestionId, levelValue, levelRAG } + ); + return numberOfAffectedRows; + } + + public int InsertCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, int levelValue, int? levelRAG) + { + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO dbo.CompetencyAssessmentQuestionRoleRequirements + (SelfAssessmentID + ,CompetencyID + ,AssessmentQuestionID + ,LevelValue + ,LevelRAG) + VALUES + (@assessmentId + ,@competencyId + ,@assessmentQuestionId + ,@levelValue + ,@levelRAG);" + , + new { assessmentId, competencyId, assessmentQuestionId, levelValue, levelRAG } + ); + return numberOfAffectedRows; + } + + public bool DeleteCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int? competencyId, int assessmentQuestionId, int levelValue) + { + var numberOfAffectedRows = connection.Execute( + @"DELETE FROM CompetencyAssessmentQuestionRoleRequirements + WHERE SelfAssessmentID = @assessmentId + AND AssessmentQuestionID = @assessmentQuestionId + AND LevelValue = @levelValue + AND (CompetencyID = @competencyId OR @competencyId IS NULL);" + , + new { assessmentId, competencyId, assessmentQuestionId, levelValue } + ); + return numberOfAffectedRows > 0; + } + public void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId) + { + var numberOfAffectedRows = connection.Execute( + @"INSERT INTO [dbo].[SelfAssessmentCollaborators] + ( + [SelfAssessmentID], + [UserEmail], + [AdminID], + [CanModify], + [IsDeleted]) + SELECT + @selfAssessmentID, + fc.UserEmail, + fc.AdminID, + fc.CanModify, + fc.IsDeleted + FROM FrameworkCollaborators fc + INNER JOIN AdminUsers au + ON fc.AdminID = au.AdminID + WHERE fc.FrameworkID = @frameworkId + AND fc.IsDeleted = 0 + AND fc.AdminID NOT IN (SELECT AdminID FROM SelfAssessmentCollaborators WHERE SelfAssessmentID = @selfAssessmentId AND IsDeleted = 0) + AND fc.AdminID NOT IN (SELECT CreatedByAdminId FROM SelfAssessmentFrameworks WHERE SelfAssessmentId = @selfAssessmentId AND FrameworkID = @frameworkId AND RemovedDate IS NULL);", + new { selfAssessmentId, frameworkId } + ); + } + public bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked) + { + var numberOfAffectedRows = connection.Execute( + @" UPDATE SelfAssessmentTaskStatus + SET SupervisorRolesTaskStatus = CASE WHEN @taskCompleteChecked = 1 THEN 1 ELSE 0 END + WHERE SelfAssessmentId = @competencyAssessmentId", + new { competencyAssessmentId, taskCompleteChecked = taskCompleteChecked ? 1 : 0 } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating SupervisorRolesTaskStatus as db update failed. " + + $"competencyAssessmentId: {competencyAssessmentId}, taskCompleteChecked: {taskCompleteChecked}" + ); + return false; + } + return true; + } + public bool UpdateSelfAssessments(int competencyAssessmentId, + int? signoff, + int? confirm, + int? supervisorDeclarationValue, + string? supervisorCustomText, + int? leanerDeclarationValue, + string? leanerCustomText + ) + { + var sqlQuery = @" + IF @signoff = 0 + BEGIN + UPDATE SelfAssessments SET SupervisorSelfAssessmentReview = 0, + SupervisorResultsReview = 0, + SignOffSupervisorStatement = NULL, + SignOffRequestorStatement = NULL + WHERE ID = @competencyAssessmentId; + END + ELSE + BEGIN + UPDATE SelfAssessments + SET SupervisorSelfAssessmentReview = 1, + SupervisorResultsReview = @confirm, + SignOffSupervisorStatement = CASE WHEN @supervisorDeclarationValue = 0 THEN NULL ELSE @supervisorCustomText END, + SignOffRequestorStatement = CASE WHEN @leanerDeclarationValue = 0 THEN NULL ELSE @leanerCustomText END + WHERE ID = @competencyAssessmentId; + END + "; + var affectedRows = connection.Execute( + sqlQuery, + new + { + competencyAssessmentId, + signoff, + confirm, + supervisorDeclarationValue, + supervisorCustomText, + leanerDeclarationValue, + leanerCustomText + } + ); + + if ((affectedRows < 1)) + { + logger.LogWarning( + "Not updating SelfAssessments as db update failed. " + + $"competencyAssessmentId: {competencyAssessmentId}, signoff: {signoff}" + + $"confirm: {confirm}, supervisorDeclarationValue: {supervisorDeclarationValue} " + + $"supervisorCustomText: {supervisorCustomText}, leanerDeclarationValue: {leanerDeclarationValue}, leanerCustomText: {leanerCustomText} " + ); + return false; + } + return true; + } + public void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId) + { + connection.Execute( + @"UPDATE SelfAssessments + SET + [PublishStatusID] = @status, + UpdatedByAdminID = @adminId + WHERE id = @competencyAssessmentId;" + , + new { competencyAssessmentId, status, adminId } + ); + } + public void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub) + { + connection.Execute( + @"UPDATE SelfAssessments + SET + [PublishStatusID] = @status, + UpdatedByAdminID = @adminId, + [National] = @national, + [Public] = @pub + WHERE id = @competencyAssessmentId;" + , + new { competencyAssessmentId, status, adminId, national, pub } + ); + } + public void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required) + { + var exists = (int?)connection.ExecuteScalar( + @"SELECT COUNT(*) + FROM SelfAssessmentReviews + WHERE SelfAssessmentID = @competencyAssessmentId + AND SelfAssessmentCollaboratorID = @selfAssessmentCollaboratorID AND Archived IS NULL", + new { competencyAssessmentId, selfAssessmentCollaboratorID } + ); + if (exists == 0) + { + connection.Query( + @"INSERT INTO SelfAssessmentReviews + (SelfAssessmentID, SelfAssessmentCollaboratorID, SignOffRequired) + VALUES + (@competencyAssessmentId, @selfAssessmentCollaboratorID, @required)", + new { competencyAssessmentId, selfAssessmentCollaboratorID, required } + ); + } + } + public IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId) + { + return connection.Query( + @"SELECT SR.ID, SR.SelfAssessmentID, SR.SelfAssessmentCollaboratorID, SC.UserEmail, + CAST(CASE WHEN SC.AdminID IS NULL THEN 0 ELSE 1 END AS bit) AS IsRegistered, + SR.ReviewRequested, SR.ReviewComplete, SR.SignedOff, SR.SelfAssessmentCommentID, SC1.Comments AS Comment, SR.SignOffRequired + FROM SelfAssessmentReviews AS SR INNER JOIN + SelfAssessmentCollaborators AS SC ON SR.SelfAssessmentCollaboratorID = SC.ID LEFT OUTER JOIN + SelfAssessmentComments AS SC1 ON SR.SelfAssessmentCommentID = SC1.ID + WHERE SR.SelfAssessmentID = @competencyAssessmentId AND SR.Archived IS NULL AND (SC.IsDeleted = 0)", + new { competencyAssessmentId } + ); + } + + public SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId) + { + return connection.Query( + @"SELECT SR.ID, SR.SelfAssessmentID, SR.SelfAssessmentCollaboratorID, SC.UserEmail, + CAST(CASE WHEN SC.AdminID IS NULL THEN 0 ELSE 1 END AS bit) AS IsRegistered, + SR.ReviewRequested, SR.ReviewComplete, SR.SignedOff, SR.SelfAssessmentCommentID, SC1.Comments AS Comment, SR.SignOffRequired + FROM SelfAssessmentReviews AS SR INNER JOIN + SelfAssessmentCollaborators AS SC ON SR.SelfAssessmentCollaboratorID = SC.ID LEFT OUTER JOIN + SelfAssessmentComments AS SC1 ON SR.SelfAssessmentCommentID = SC1.ID + WHERE SR.SelfAssessmentID = @competencyAssessmentId AND SR.ID = @selfAssessmentReviewId AND SR.Archived IS NULL AND (SC.IsDeleted = 0)", + new { competencyAssessmentId, selfAssessmentReviewId } + ).FirstOrDefault(); + } + public void ArchiveSelfAssessmentReviewRequest(int reviewId) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentReviews + SET Archived = GETUTCDATE() + WHERE ID = @reviewId", + new { reviewId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not archiving framework review as db update failed. " + + $"reviewId: {reviewId}." + ); + } + } + public void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentReviews + SET ReviewComplete = GETUTCDATE(), SelfAssessmentCommentID = @commentId, SignedOff = @signedOff + WHERE ID = @reviewId AND SelfAssessmentID = @selfAssessmentID", + new { reviewId, commentId, signedOff, selfAssessmentID } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not submitting selfAssessment review as db update failed. " + + $"commentId: {commentId}, frameworkId: {selfAssessmentID}, reviewId: {reviewId}, signedOff: {signedOff} ." + ); + } + } + public int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId) + { + if ((selfAssessmentID < 1) | (adminId < 1) | (comment == null)) + { + logger.LogWarning( + $"Not inserting selfAssessment comment as it failed server side validation. AdminId: {adminId}, selfAssessmentID: {selfAssessmentID}, comment: {comment}" + ); + } + + var commentId = connection.ExecuteScalar( + @"INSERT INTO SelfAssessmentComments + (AdminID + ,ReplyToSelfAssessmentCommentID + ,Comments + ,SelfAssessmentID) + VALUES (@adminId, @replyToCommentId, @comment, @selfAssessmentID); + SELECT CAST(SCOPE_IDENTITY() as int)", + new { adminId, replyToCommentId, comment, selfAssessmentID } + ); + if (commentId < 1) + { + logger.LogWarning( + "Not inserting selfAssessment comment as db insert failed. " + + $"AdminId: {adminId}, selfAssessmentID: {selfAssessmentID}, comment: {comment}." + ); + } + + return commentId; + } + public SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId) + { + return connection.Query( + @"SELECT + sr.ID, + sr.SelfAssessmentID, + sr.SelfAssessmentCollaboratorID, + sc.UserEmail, + CAST(CASE WHEN sc.AdminID IS NULL THEN 0 ELSE 1 END AS bit) AS IsRegistered, + sr.ReviewRequested, + sr.ReviewComplete, + sr.SignedOff, + sr.SelfAssessmentCommentID, + sc1.Comments AS Comment, + sr.SignOffRequired, + AU.Forename AS ReviewerFirstName, + AU.Surname AS ReviewerLastName, + AU.Active AS ReviewerActive, + AU1.Forename AS OwnerFirstName, + AU1.Email AS OwnerEmail, + s.Name AS SelfAssessmentName + FROM SelfAssessmentReviews AS sr + INNER JOIN SelfAssessmentCollaborators AS sc ON sr.SelfAssessmentCollaboratorID = sc.ID AND sc.IsDeleted = 0 + INNER JOIN AdminUsers AS AU ON sc.AdminID = AU.AdminID + INNER JOIN SelfAssessments AS s ON sr.SelfAssessmentID = s.ID + INNER JOIN AdminUsers AS AU1 ON s.CreatedByAdminID = AU1.AdminID + LEFT OUTER JOIN SelfAssessmentComments AS sc1 ON sr.SelfAssessmentCommentID = sc1.ID + WHERE (sr.ID = @reviewId) AND (sr.ReviewComplete IS NOT NULL)", + new { reviewId } + ).FirstOrDefault(); + } + public void UpdateReviewRequestedDate(int reviewId) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentReviews + SET ReviewRequested = GETUTCDATE() + WHERE ID = @reviewId", + new { reviewId } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating selfAssessment review requested date as db update failed. " + + $"reviewId: {reviewId}." + ); + } + } + public int InsertCompetencySelfAssessmentReview(int reviewId) + { + ArchiveSelfAssessmentReviewRequest(reviewId); + var id = connection.QuerySingle( + @"INSERT INTO SelfAssessmentReviews + (SelfAssessmentID, SelfAssessmentCollaboratorID, SignOffRequired) + OUTPUT INSERTED.ID + SELECT sr1.SelfAssessmentID, sr1.SelfAssessmentCollaboratorID, sr1.SignOffRequired FROM SelfAssessmentReviews AS sr1 WHERE sr1.ID = @reviewId", + new { reviewId } + ); + if (id < 1) + { + logger.LogWarning( + "Not inserting assessment question as db update failed. " + + $"reviewId: {reviewId}" + ); + return 0; + } + + return id; + } + public bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentTaskStatus SET ReviewTaskStatus = @taskStatus + WHERE SelfAssessmentId = @assessmentId", + new { assessmentId, taskStatus } + ); + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + "Not updating ReviewTaskStatus as db update failed. " + + $"assessmentId: {assessmentId}, taskStatus: {taskStatus}" + ); + return false; + } + return true; + } + } +} diff --git a/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs b/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs index 323188bf45..f00b2b1a71 100644 --- a/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs @@ -14,6 +14,7 @@ public interface ICompetencyLearningResourcesDataService IEnumerable GetCompetencyResourceAssessmentQuestionParameters(IEnumerable competencyLearningResourceIds); int AddCompetencyLearningResource(int resourceRefID, string originalResourceName, string description, string resourceType, string link, string catalogue, decimal rating, int competencyID, int adminId); + IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId); } public class CompetencyLearningResourcesDataService : ICompetencyLearningResourcesDataService @@ -120,5 +121,20 @@ FROM CompetencyResourceAssessmentQuestionParameters new { competencyLearningResourceIds } ); } + public IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId) + { + return connection.Query( + @"SELECT + clr.ID, + clr.CompetencyID, + clr.LearningResourceReferenceID, + clr.AdminID, + lrr.ResourceRefID AS LearningHubResourceReferenceId + FROM CompetencyLearningResources AS clr + INNER JOIN LearningResourceReferences AS lrr ON lrr.ID = clr.LearningResourceReferenceID + WHERE CompetencyID = @competencyId AND ResourceRefID = @referenceId AND clr.RemovedDate IS NULL", + new { competencyId, referenceId } + ); + } } } diff --git a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs index 06196293a0..f7c387cf96 100644 --- a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs @@ -122,7 +122,7 @@ IEnumerable GetCourseDelegatesForExport(string searchSt int customisationId, int centreId, bool? isDelegateActive, bool? isProgressLocked, bool? removed, bool? hasCompleted, string? answer1, string? answer2, string? answer3); int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail, - int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId); + int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId, bool isNonReportable = false); bool IsCourseCompleted(int candidateId, int customisationId); bool IsCourseCompleted(int candidateId, int customisationId, int progressID); @@ -443,7 +443,7 @@ public void RemoveCurrentCourse(int progressId, int candidateId, RemovalMethod r } public int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail, - int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId) + int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId, bool isNonReportable = false) { IClockUtility clockUtility = new ClockUtility(); DateTime startedDate = clockUtility.UtcNow; @@ -473,7 +473,8 @@ FROM CandidateAssessments ,[CompleteByDate] ,[CentreID] ,[EnrolmentMethodId] - ,[EnrolledByAdminId]) + ,[EnrolledByAdminId] + ,[NonReportable]) OUTPUT INSERTED.Id VALUES (@DelegateUserID, @@ -483,8 +484,9 @@ OUTPUT INSERTED.Id @completeByDateDynamic, @centreId, @enrolmentMethodId, - @enrolledByAdminId);", - new { delegateUserId, selfAssessmentId, startedDate, lastAccessed, completeByDateDynamic, centreId, enrolmentMethodId, enrolledByAdminId } + @enrolledByAdminId, + @isNonReportable);", + new { delegateUserId, selfAssessmentId, startedDate, lastAccessed, completeByDateDynamic, centreId, enrolmentMethodId, enrolledByAdminId, isNonReportable } ); } @@ -514,7 +516,8 @@ INNER JOIN Users AS U LEFT OUTER JOIN UserCentreDetails AS UCD ON DA.UserID = UCD.UserID AND DA.CentreID = UCD.CentreID - WHERE (DA.UserID = @delegateUserId)", new { supervisorId, delegateUserId, adminEmail }); + WHERE (DA.UserID = @delegateUserId AND DA.CentreID = @centreId)", + new { supervisorId, delegateUserId, adminEmail, centreId }); } if (candidateAssessmentId > 0 && supervisorDelegateId > 0 && selfAssessmentSupervisorRoleId > 0) diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs index c5ff0af01f..2c7d07013a 100644 --- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs @@ -5,6 +5,7 @@ using System.Data; using System.Linq; using Dapper; + using DigitalLearningSolutions.Data.Extensions; using DigitalLearningSolutions.Data.Models.Common; using DigitalLearningSolutions.Data.Models.Email; using DigitalLearningSolutions.Data.Models.Frameworks; @@ -51,9 +52,9 @@ public interface IFrameworkDataService CollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId); // Competencies/groups: - IEnumerable GetFrameworkCompetencyGroups(int frameworkId); + IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId); - IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId); + IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId); CompetencyGroupBase? GetCompetencyGroupBaseById(int Id); @@ -267,6 +268,10 @@ bool deleteFromExisting void DeleteCompetencyLearningResource(int competencyLearningResourceId, int adminId); void UpdateFrameworkCompetencyFrameworkCompetencyGroup(int? competencyGroupId, int frameworkCompetencyGroupId, int adminId); + + void ChangeFrameworkOwner(int frameworkId, string newOwnerEmail, int updatedByAdminId); + IEnumerable GetAdminIdsForUserId(int userId); + } public class FrameworkDataService : IFrameworkDataService @@ -310,7 +315,7 @@ FROM FrameworkCollaborators fc AND aa3.Active = 1 ORDER BY fwr.ID DESC) AS FrameworkReviewID"; private const string BrandedFrameworkFields = - @",(SELECT BrandName + @", FW.Description, FW.FrameworkConfig AS Vocabulary, (SELECT BrandName FROM Brands WHERE (BrandID = FW.BrandID)) AS Brand, (SELECT CategoryName @@ -861,18 +866,23 @@ public void RemoveCustomFlag(int flagId) ); } - public IEnumerable GetFrameworkCompetencyGroups(int frameworkId) + public IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId) { + var assessmentFilter = assessmentId.HasValue ? + @$"AND c.ID NOT IN (SELECT CompetencyID + FROM SelfAssessmentStructure + WHERE (SelfAssessmentID = {assessmentId}))" + : string.Empty; var result = connection.Query( - @"SELECT fcg.ID, fcg.CompetencyGroupID, cg.Name, cg.Description, fcg.Ordering, fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering, COUNT(caq.AssessmentQuestionID) AS AssessmentQuestions + @$"SELECT fcg.ID, fcg.CompetencyGroupID, cg.Name, fcg.Ordering, fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering, + (SELECT COUNT(*) FROM CompetencyAssessmentQuestions caq WHERE caq.CompetencyID = c.ID) AS AssessmentQuestions ,(SELECT COUNT(*) FROM CompetencyLearningResources clr WHERE clr.CompetencyID = c.ID AND clr.RemovedDate IS NULL) AS CompetencyLearningResourcesCount FROM FrameworkCompetencyGroups AS fcg INNER JOIN CompetencyGroups AS cg ON fcg.CompetencyGroupID = cg.ID LEFT OUTER JOIN FrameworkCompetencies AS fc ON fcg.ID = fc.FrameworkCompetencyGroupID LEFT OUTER JOIN - Competencies AS c ON fc.CompetencyID = c.ID LEFT OUTER JOIN - CompetencyAssessmentQuestions AS caq ON c.ID = caq.CompetencyID - WHERE (fcg.FrameworkID = @frameworkId) - GROUP BY fcg.ID, fcg.CompetencyGroupID, cg.Name, cg.Description, fcg.Ordering, fc.ID, c.ID, c.Name, c.Description, fc.Ordering + Competencies AS c ON fc.CompetencyID = c.ID + WHERE (fcg.FrameworkID = @frameworkId) {assessmentFilter} + GROUP BY fcg.ID, fcg.CompetencyGroupID, cg.Name, fcg.Ordering, fc.ID, c.ID, c.Name, c.Description, fc.Ordering ORDER BY fcg.Ordering, fc.Ordering", (frameworkCompetencyGroup, frameworkCompetency) => { @@ -882,29 +892,36 @@ FROM FrameworkCompetencyGroups AS fcg INNER JOIN }, new { frameworkId } ); - return result.GroupBy(frameworkCompetencyGroup => frameworkCompetencyGroup.CompetencyGroupID).Select( - group => - { - var groupedFrameworkCompetencyGroup = group.First(); - groupedFrameworkCompetencyGroup.FrameworkCompetencies = group.Where(frameworkCompetencyGroup => frameworkCompetencyGroup.FrameworkCompetencies.Count > 0) - .Select( - frameworkCompetencyGroup => frameworkCompetencyGroup.FrameworkCompetencies.Single() - ).ToList(); - return groupedFrameworkCompetencyGroup; - } - ); + return result + .GroupBy(fcg => fcg.CompetencyGroupID) + .Select(group => + { + var groupedFrameworkCompetencyGroup = group.First(); + + // Flatten all FrameworkCompetencies from all instances in this group + groupedFrameworkCompetencyGroup.FrameworkCompetencies = group + .SelectMany(g => g.FrameworkCompetencies) + .Where(fc => fc != null). + Distinct().ToList(); + + return groupedFrameworkCompetencyGroup; + }); } - public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId) + public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId) { + var assessmentFilter = assessmentId.HasValue ? + @$"AND c.ID NOT IN (SELECT CompetencyID + FROM SelfAssessmentStructure + WHERE (SelfAssessmentID = {assessmentId}))" + : string.Empty; return connection.Query( - @"SELECT fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering, COUNT(caq.AssessmentQuestionID) AS AssessmentQuestions,(select COUNT(CompetencyId) from CompetencyLearningResources where CompetencyID=c.ID) AS CompetencyLearningResourcesCount + @$"SELECT fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering, + (SELECT COUNT(*) FROM CompetencyAssessmentQuestions caq WHERE caq.CompetencyID = c.ID) AS AssessmentQuestions,(select COUNT(CompetencyId) from CompetencyLearningResources where CompetencyID=c.ID) AS CompetencyLearningResourcesCount FROM FrameworkCompetencies AS fc INNER JOIN Competencies AS c ON fc.CompetencyID = c.ID - LEFT OUTER JOIN - CompetencyAssessmentQuestions AS caq ON c.ID = caq.CompetencyID WHERE fc.FrameworkID = @frameworkId - AND fc.FrameworkCompetencyGroupID IS NULL + AND fc.FrameworkCompetencyGroupID IS NULL {assessmentFilter} GROUP BY fc.ID, c.ID, c.Name, c.Description, fc.Ordering ORDER BY fc.Ordering", new { frameworkId } @@ -2067,7 +2084,7 @@ FROM FrameworkComments public IEnumerable GetReviewersForFrameworkId(int frameworkId) { return connection.Query( - @"SELECT + @"SELECT DISTINCT fc.ID, fc.FrameworkID, fc.AdminID, @@ -2078,8 +2095,11 @@ public IEnumerable GetReviewersForFrameworkId(int frameworkI FROM FrameworkCollaborators fc INNER JOIN AdminUsers AS au ON fc.AdminID = au.AdminID LEFT OUTER JOIN FrameworkReviews ON fc.ID = FrameworkReviews.FrameworkCollaboratorID - WHERE (fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.ID IS NULL) AND (fc.IsDeleted=0) OR - (fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.Archived IS NOT NULL) AND (fc.IsDeleted=0)", + WHERE ((fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.ID IS NULL) AND (fc.IsDeleted=0)) OR + ((fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.Archived IS NOT NULL) AND (fc.IsDeleted=0)) + AND + (fc.ID Not in ( Select FrameworkCollaboratorID from FrameworkReviews where FrameworkID = @FrameworkID AND + FrameworkReviews.Archived IS NULL AND FrameworkCollaboratorID = fc.ID))", new { frameworkId } ); } @@ -2261,7 +2281,7 @@ public void ArchiveReviewRequest(int reviewId) FROM FrameworkCollaborators WHERE (FrameworkID = FW.ID) AND (IsDeleted=0)))) AS MyFrameworksCount, - (SELECT COUNT(*) FROM SelfAssessments) AS RoleProfileCount, + (SELECT COUNT(*) FROM SelfAssessments) AS CompetencyAssessmentCount, (SELECT COUNT(*) FROM SelfAssessments AS RP LEFT OUTER JOIN SelfAssessmentCollaborators AS RPC ON RPC.SelfAssessmentID = RP.ID AND RPC.AdminID = @adminId @@ -2269,7 +2289,7 @@ FROM FrameworkCollaborators (@adminId IN (SELECT AdminID FROM SelfAssessmentCollaborators - WHERE (SelfAssessmentID = RP.ID)))) AS MyRoleProfileCount", + WHERE (SelfAssessmentID = RP.ID)))) AS MyCompetencyAssessmentCount", new { adminId } ).FirstOrDefault(); } @@ -2279,7 +2299,7 @@ public IEnumerable GetDashboardToDoItems(int adminId) return connection.Query( @"SELECT FW.ID AS FrameworkID, - 0 AS RoleProfileID, + 0 AS SelfAssessmentID, FW.FrameworkName AS ItemName, AU.Forename + ' ' + AU.Surname + (CASE WHEN AU.Active = 1 THEN '' ELSE ' (Inactive)' END) AS RequestorName, FWR.SignOffRequired, @@ -2291,8 +2311,8 @@ FROM FrameworkReviews AS FWR WHERE (FWC.AdminID = @adminId) AND (FWR.ReviewComplete IS NULL) AND (FWR.Archived IS NULL) UNION ALL SELECT - 0 AS SelfAssessmentID, - RP.ID AS SelfAssessmentID, + 0 AS FrameworkID, + RP.ID AS CompetencyAssessmentID, RP.Name AS ItemName, AU.Forename + ' ' + AU.Surname + (CASE WHEN AU.Active = 1 THEN '' ELSE ' (Inactive)' END) AS RequestorName, RPR.SignOffRequired, @@ -2549,5 +2569,51 @@ public void UpdateFrameworkCompetencyFrameworkCompetencyGroup(int? competencyGro ); } } + + public void ChangeFrameworkOwner(int frameworkId, string newOwnerEmail, int updatedByAdminId) + { + // Find the AdminID for the new owner + var newOwnerAdminId = connection.Query( + @"SELECT AdminID FROM AdminUsers WHERE Email = @newOwnerEmail AND Active = 1", + new { newOwnerEmail } + ).FirstOrDefault(); ; + if (newOwnerAdminId == null) + { + throw new Exception("No active admin user found with that email."); + } + + // Update the owner + connection.EnsureOpen(); + using var transaction = connection.BeginTransaction(); + { + var rows = connection.Execute( + @"UPDATE Frameworks SET OwnerAdminID = @newOwnerAdminId, UpdatedByAdminID = @updatedByAdminId WHERE ID = @frameworkId", + new { frameworkId, newOwnerAdminId, updatedByAdminId } + , transaction + ); + connection.Execute( + @"UPDATE FrameworkCollaborators + SET IsDeleted = 1 + WHERE FrameworkID = @frameworkId + AND AdminID = @newOwnerAdminId + AND IsDeleted = 0;", + new { frameworkId, newOwnerAdminId }, + transaction + ); + transaction.Commit(); + + if (rows < 1) + { + throw new Exception("Failed to update framework owner."); + } + } + } + public IEnumerable GetAdminIdsForUserId(int userId) + { + return connection.Query( + @"SELECT ID FROM AdminAccounts WHERE UserID = @userId", + new { userId } + ); + } } } diff --git a/DigitalLearningSolutions.Data/DataServices/RoleProfileDataService.cs b/DigitalLearningSolutions.Data/DataServices/RoleProfileDataService.cs deleted file mode 100644 index 7e37d4ff03..0000000000 --- a/DigitalLearningSolutions.Data/DataServices/RoleProfileDataService.cs +++ /dev/null @@ -1,197 +0,0 @@ -namespace DigitalLearningSolutions.Data.DataServices -{ - using System.Collections.Generic; - using System.Data; - using System.Linq; - using Dapper; - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using Microsoft.Extensions.Logging; - - public interface IRoleProfileDataService - { - //GET DATA - IEnumerable GetAllRoleProfiles(int adminId); - - IEnumerable GetRoleProfilesForAdminId(int adminId); - - RoleProfileBase? GetRoleProfileBaseById(int roleProfileId, int adminId); - - RoleProfileBase? GetRoleProfileByName(string roleProfileName, int adminId); - - IEnumerable GetNRPProfessionalGroups(); - - //UPDATE DATA - bool UpdateRoleProfileName(int roleProfileId, int adminId, string roleProfileName); - - bool UpdateRoleProfileProfessionalGroup(int roleProfileId, int adminId, int? nrpProfessionalGroupID); - //INSERT DATA - - //DELETE DATA - } - - public class RoleProfileDataService : IRoleProfileDataService - { - private const string SelfAssessmentBaseFields = @"rp.ID, rp.Name AS RoleProfileName, rp.Description, rp.BrandID, - rp.ParentSelfAssessmentID, - rp.[National], rp.[Public], rp.CreatedByAdminID AS OwnerAdminID, - rp.NRPProfessionalGroupID, - rp.NRPSubGroupID, - rp.NRPRoleID, - rp.PublishStatusID, CASE WHEN rp.CreatedByAdminID = @adminId THEN 3 WHEN rpc.CanModify = 1 THEN 2 WHEN rpc.CanModify = 0 THEN 1 ELSE 0 END AS UserRole"; - - private const string SelfAssessmentFields = - @", rp.CreatedDate, - (SELECT BrandName - FROM Brands - WHERE (BrandID = rp.BrandID)) AS Brand, - (SELECT [Name] - FROM SelfAssessments AS rp2 - WHERE (ID = rp.ParentSelfAssessmentID)) AS ParentSelfAssessment, - (SELECT Forename + ' ' + Surname + (CASE WHEN Active = 1 THEN '' ELSE ' (Inactive)' END) AS Expr1 - FROM AdminUsers - WHERE (AdminID = rp.CreatedByAdminID)) AS Owner, - rp.Archived, - rp.LastEdit, - (SELECT ProfessionalGroup - FROM NRPProfessionalGroups - WHERE (ID = rp.NRPProfessionalGroupID)) AS NRPProfessionalGroup, - (SELECT SubGroup - FROM NRPSubGroups - WHERE (ID = rp.NRPSubGroupID)) AS NRPSubGroup, - (SELECT RoleProfile - FROM NRPRoles - WHERE (ID = rp.NRPRoleID)) AS NRPRole, rpr.ID AS SelfAssessmentReviewID"; - - private const string SelfAssessmentBaseTables = - @"SelfAssessments AS rp LEFT OUTER JOIN - SelfAssessmentCollaborators AS rpc ON rpc.SelfAssessmentID = rp.ID AND rpc.AdminID = @adminId"; - - private const string SelfAssessmentTables = - @" LEFT OUTER JOIN - SelfAssessmentReviews AS rpr ON rpc.ID = rpr.SelfAssessmentCollaboratorID AND rpr.Archived IS NULL AND rpr.ReviewComplete IS NULL"; - - private readonly IDbConnection connection; - private readonly ILogger logger; - - public RoleProfileDataService(IDbConnection connection, ILogger logger) - { - this.connection = connection; - this.logger = logger; - } - - public IEnumerable GetAllRoleProfiles(int adminId) - { - return connection.Query( - $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields} - FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}", - new { adminId } - ); - } - - public IEnumerable GetRoleProfilesForAdminId(int adminId) - { - return connection.Query( - $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields} - FROM {SelfAssessmentBaseTables} {SelfAssessmentTables} - WHERE (rp.CreatedByAdminID = @adminId) OR - (@adminId IN - (SELECT AdminID - FROM SelfAssessmentCollaborators - WHERE (SelfAssessmentID = rp.ID)))", - new { adminId } - ); - } - - public RoleProfileBase? GetRoleProfileBaseById(int roleProfileId, int adminId) - { - return connection.Query( - $@"SELECT {SelfAssessmentBaseFields} - FROM {SelfAssessmentBaseTables} - WHERE (rp.ID = @roleProfileId)", - new { roleProfileId, adminId } - ).FirstOrDefault(); - } - - public bool UpdateRoleProfileName(int roleProfileId, int adminId, string roleProfileName) - { - if ((roleProfileName.Length == 0) | (adminId < 1) | (roleProfileId < 1)) - { - logger.LogWarning( - $"Not updating role profile name as it failed server side validation. AdminId: {adminId}, roleProfileName: {roleProfileName}, roleProfileId: {roleProfileId}" - ); - return false; - } - - var existingSelfAssessments = (int)connection.ExecuteScalar( - @"SELECT COUNT(*) FROM SelfAssessments WHERE [Name] = @roleProfileName AND ID <> @roleProfileId", - new { roleProfileName, roleProfileId } - ); - if (existingSelfAssessments > 0) - { - return false; - } - - var numberOfAffectedRows = connection.Execute( - @"UPDATE SelfAssessments SET [Name] = @roleProfileName, UpdatedByAdminID = @adminId - WHERE ID = @roleProfileId", - new { roleProfileName, adminId, roleProfileId } - ); - if (numberOfAffectedRows < 1) - { - logger.LogWarning( - "Not updating role profile name as db update failed. " + - $"SelfAssessmentName: {roleProfileName}, admin id: {adminId}, roleProfileId: {roleProfileId}" - ); - return false; - } - - return true; - } - - public IEnumerable GetNRPProfessionalGroups() - { - return connection.Query( - @"SELECT ID, ProfessionalGroup, Active - FROM NRPProfessionalGroups - WHERE (Active = 1) - ORDER BY ProfessionalGroup" - ); - } - - public RoleProfileBase? GetRoleProfileByName(string roleProfileName, int adminId) - { - return connection.Query( - $@"SELECT {SelfAssessmentBaseFields} - FROM {SelfAssessmentBaseTables} - WHERE (rp.Name = @roleProfileName)", - new { roleProfileName, adminId } - ).FirstOrDefault(); - } - - public bool UpdateRoleProfileProfessionalGroup(int roleProfileId, int adminId, int? nrpProfessionalGroupID) - { - var sameCount = (int)connection.ExecuteScalar( - @"SELECT COUNT(*) FROM RoleProfiles WHERE ID = @roleProfileId AND NRPProfessionalGroupID = @nrpProfessionalGroupID", - new { roleProfileId, nrpProfessionalGroupID } - ); - if (sameCount > 0) - { - //same so don't update: - return false; - } - - //needs updating: - var numberOfAffectedRows = connection.Execute( - @"UPDATE SelfAssessments SET NRPProfessionalGroupID = @nrpProfessionalGroupID, NRPSubGroupID = NULL, NRPRoleID = NULL, UpdatedByAdminID = @adminId - WHERE ID = @roleProfileId", - new { nrpProfessionalGroupID, adminId, roleProfileId } - ); - if (numberOfAffectedRows > 0) - { - return true; - } - - return false; - } - } -} diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs index 4f3677edca..90f8133a56 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs @@ -186,6 +186,7 @@ CandidateAssessments AS CA LEFT OUTER JOIN SA.SupervisorSelfAssessmentReview, SA.ReviewerCommentsLabel, SA.EnforceRoleRequirementsForSignOff, + SA.RetirementDate, COALESCE(SA.Vocabulary, 'Capability') AS Vocabulary, COUNT(C.ID) AS NumberOfCompetencies, CA.StartedDate, @@ -202,7 +203,7 @@ CandidateAssessments AS CA LEFT OUTER JOIN CAST(CASE WHEN CA.SelfAssessmentProcessAgreed IS NOT NULL THEN 1 ELSE 0 END AS BIT) AS SelfAssessmentProcessAgreed, CAST(CASE WHEN SA.SupervisorSelfAssessmentReview = 1 OR SA.SupervisorResultsReview = 1 THEN 1 ELSE 0 END AS BIT) AS IsSupervised, CASE - WHEN (SELECT COUNT(*) FROM SelfAssessmentSupervisorRoles WHERE SelfAssessmentID = @selfAssessmentId AND AllowDelegateNomination = 1) > 0 + WHEN (SELECT COUNT(*) FROM SelfAssessmentSupervisorRoles WHERE SelfAssessmentID = @selfAssessmentId AND AllowDelegateNomination = 1) > 0 THEN 1 ELSE 0 END AS HasDelegateNominatedRoles, @@ -210,7 +211,7 @@ ELSE 0 (SELECT TOP (1) RoleName FROM SelfAssessmentSupervisorRoles WHERE (ResultsReview = 1) AND (SelfAssessmentID = @selfAssessmentId) AND ((SELECT COUNT(*) AS Expr1 FROM SelfAssessmentSupervisorRoles AS SelfAssessmentSupervisorRoles_1 - WHERE (ResultsReview = 1) AND (SelfAssessmentID = @selfAssessmentId)) = 1)), + WHERE (ResultsReview = 1) AND (SelfAssessmentID = @selfAssessmentId)) = 1)), 'Supervisor') AS VerificationRoleName, COALESCE( (SELECT TOP (1) RoleName FROM SelfAssessmentSupervisorRoles @@ -223,7 +224,8 @@ FROM SelfAssessmentSupervisorRoles AS SelfAssessmentSupervisorRoles_1 SA.ManageSupervisorsDescription, CA.NonReportable, U.FirstName +' '+ U.LastName AS DelegateName, - SA.MinimumOptionalCompetencies + SA.MinimumOptionalCompetencies, + SA.IncludeLearnerDeclarationPrompt FROM CandidateAssessments CA JOIN SelfAssessments SA ON CA.SelfAssessmentID = SA.ID @@ -248,7 +250,8 @@ GROUP BY CA.LaunchCount, CA.SubmittedDate, SA.LinearNavigation, SA.UseDescriptionExpanders, SA.ManageOptionalCompetenciesPrompt, SA.SupervisorSelfAssessmentReview, SA.SupervisorResultsReview, SA.ReviewerCommentsLabel,SA.EnforceRoleRequirementsForSignOff, SA.ManageSupervisorsDescription,CA.NonReportable, - U.FirstName , U.LastName,SA.MinimumOptionalCompetencies, CA.SelfAssessmentProcessAgreed", + U.FirstName , U.LastName,SA.MinimumOptionalCompetencies, CA.SelfAssessmentProcessAgreed, SA.IncludeLearnerDeclarationPrompt, + SA.RetirementDate", new { delegateUserId, selfAssessmentId } ); } diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs index 3c333e78e8..aa28da3bfa 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs @@ -100,7 +100,7 @@ INNER JOIN FrameworkCompetencies AS FC WHERE FC.CompetencyID = C.ID), 'Capability') AS Vocabulary, CASE - WHEN (SELECT COUNT(*) FROM SelfAssessmentSupervisorRoles WHERE SelfAssessmentID = SAS.SelfAssessmentID) > 0 + WHEN (SELECT COUNT(*) FROM SelfAssessmentSupervisorRoles WHERE SelfAssessmentID = SAS.SelfAssessmentID) > 0 THEN 1 ELSE 0 END AS HasDelegateNominatedRoles, @@ -642,7 +642,7 @@ FROM SelfAssessmentResults s inner join public void RemoveReviewCandidateAssessmentOptionalCompetencies(int id) { - + connection.Execute( @"delete from SelfAssessmentResultSupervisorVerifications WHERE SelfAssessmentResultId = @id", new { id }); diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs index bc47f8eb21..c01d1ec1b1 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs @@ -296,7 +296,12 @@ FROM SelfAssessments COALESCE(ucd.Email, u.PrimaryEmail) AS DelegateEmail, da.Active AS IsDelegateActive, sa.Name AS [Name], - MAX(casv.Verified) as SignedOff, + CASE + WHEN MAX(sar.[DateTime]) >= MAX(casv.Verified) THEN NULL + ELSE ( + MAX(casv.Verified) + ) + END AS SignedOff, sa.SupervisorSelfAssessmentReview, sa.SupervisorResultsReview"; @@ -311,6 +316,8 @@ LEFT OUTER JOIN Users AS uEnrolledBy WITH (NOLOCK) ON uEnrolledBy.ID = aaEnrolle LEFT JOIN dbo.CandidateAssessmentSupervisors AS cas WITH (NOLOCK) ON ca.ID = cas.CandidateAssessmentID LEFT JOIN dbo.CandidateAssessmentSupervisorVerifications AS casv WITH (NOLOCK) ON cas.ID = casv.CandidateAssessmentSupervisorID AND (casv.Verified IS NOT NULL AND casv.SignedOff = 1) + LEFT JOIN SelfAssessmentResults AS sar ON ca.SelfAssessmentID = sar.SelfAssessmentID AND + ca.DelegateUserID = sar.DelegateUserID WHERE sa.ID = @selfAssessmentId AND da.CentreID = @centreID AND csa.CentreID = @centreID @@ -349,7 +356,8 @@ LEFT JOIN dbo.CandidateAssessmentSupervisorVerifications AS casv WITH (NOLOCK) O if (signedOff != null) { - groupBy += (bool)signedOff ? " HAVING MAX(casv.Verified) IS NOT NULL " : " HAVING MAX(casv.Verified) IS NULL "; + groupBy += (bool)signedOff ? " HAVING MAX(sar.[DateTime]) < MAX(casv.Verified) AND MAX(casv.Verified) IS NOT NULL " : + " HAVING MAX(sar.[DateTime]) >= MAX(casv.Verified) OR MAX(casv.Verified) IS NULL "; } string orderBy; diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs index 2a77222937..8d839f786a 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs @@ -110,6 +110,7 @@ int delegateUserId return connection.Query( @"SELECT DISTINCT sd.ID AS SupervisorDelegateID, + au.AdminUserID AS SupervisorAdminUserID, sd.SupervisorAdminID, sd.SupervisorEmail, sd.NotificationSent, @@ -217,6 +218,7 @@ int delegateUserId return connection.Query( @"SELECT AdminID, + AdminUserID, Forename, Surname, Active, diff --git a/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs b/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs index 0abb090205..d0553153ee 100644 --- a/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Data.DataServices { using Dapper; - using DigitalLearningSolutions.Data.Models.RoleProfiles; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; using DigitalLearningSolutions.Data.Models.SelfAssessments; using DigitalLearningSolutions.Data.Models.Supervisor; using Microsoft.Extensions.Logging; @@ -23,8 +23,8 @@ public interface ISupervisorDataService IEnumerable GetSupervisorDashboardToDoItemsForRequestedSignOffs(int adminId); IEnumerable GetSupervisorDashboardToDoItemsForRequestedReviews(int adminId); DelegateSelfAssessment? GetSelfAssessmentBaseByCandidateAssessmentId(int candidateAssessmentId, int? adminIdCategoryId); - IEnumerable GetAvailableRoleProfilesForDelegate(int candidateId, int centreId, int? categoryId); - RoleProfile? GetRoleProfileById(int selfAssessmentId); + IEnumerable GetAvailableCompetencyAssessmentsForDelegate(int candidateId, int centreId, int? categoryId); + CompetencyAssessment? GetCompetencyAssessmentById(int selfAssessmentId); IEnumerable GetSupervisorRolesForSelfAssessment(int selfAssessmentId); IEnumerable GetSupervisorRolesBySelfAssessmentIdForSupervisor(int selfAssessmentId); IEnumerable GetDelegateNominatableSupervisorRolesForSelfAssessment(int selfAssessmentId); @@ -39,7 +39,6 @@ public interface ISupervisorDataService IEnumerable GetSupervisorDelegateDetailsForAdminIdWithoutRemovedClause(int adminId); SupervisorDelegateDetail GetSupervisorDelegateDetailsByIdWithoutRemoveClause(int supervisorDelegateId, int adminId, int delegateUserId); //UPDATE DATA - bool ConfirmSupervisorDelegateById(int supervisorDelegateId, int candidateId, int adminId); bool RemoveSupervisorDelegateById(int supervisorDelegateId, int delegateUserId, int adminId); bool UpdateSelfAssessmentResultSupervisorVerifications(int selfAssessmentResultSupervisorVerificationId, string? comments, bool signedOff, int adminId); bool UpdateSelfAssessmentResultSupervisorVerificationsEmailSent(int selfAssessmentResultSupervisorVerificationId); @@ -106,16 +105,33 @@ INNER JOIN AdminAccounts AS aa "; private const string delegateSelfAssessmentFields = "ca.ID, sa.ID AS SelfAssessmentID, sa.Name AS RoleName, sa.SupervisorSelfAssessmentReview, sa.SupervisorResultsReview, COALESCE (sasr.RoleName, 'Supervisor') AS SupervisorRoleTitle, ca.StartedDate"; - private const string signedOffFields = @"(SELECT TOP (1) casv.Verified -FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN - CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID -WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL) -ORDER BY casv.Requested DESC) AS SignedOffDate, -(SELECT TOP(1) casv.SignedOff -FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN - CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID -WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL) -ORDER BY casv.Requested DESC) AS SignedOff,"; + private const string signedOffFields = @"(SELECT CASE + WHEN MAX(sar.[DateTime]) >= MAX(casv.Verified) THEN NULL + ELSE MAX(casv.Verified) + END AS Verified + FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN + CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID INNER JOIN + CandidateAssessments AS ca1 ON cas.CandidateAssessmentID = ca1.ID LEFT JOIN + SelfAssessmentResults AS sar ON ca1.SelfAssessmentID = sar.SelfAssessmentID AND ca1.DelegateUserID = sar.DelegateUserID + WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL) + GROUP BY ca1.ID ) AS SignedOffDate, + + (SELECT CASE + WHEN MAX(sar.[DateTime]) >= MAX(casv.Verified) THEN NULL + ELSE ( + SELECT TOP(1) casv.SignedOff + FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN + CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID + WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL) + ORDER BY casv.Verified DESC + ) + END AS SignedOff + FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN + CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID INNER JOIN + CandidateAssessments AS ca1 ON cas.CandidateAssessmentID = ca1.ID LEFT JOIN + SelfAssessmentResults AS sar ON ca1.SelfAssessmentID = sar.SelfAssessmentID AND ca1.DelegateUserID = sar.DelegateUserID + WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL) + GROUP BY ca1.ID) AS SignedOff,"; public SupervisorDataService(IDbConnection connection, ILogger logger) { @@ -126,59 +142,63 @@ public SupervisorDataService(IDbConnection connection, ILogger( - @"SELECT (SELECT COUNT(sd.ID) AS StaffCount - FROM CustomPrompts AS cp6 - RIGHT OUTER JOIN CustomPrompts AS cp5 - RIGHT OUTER JOIN DelegateAccounts AS da - RIGHT OUTER JOIN SupervisorDelegates AS sd - INNER JOIN AdminUsers AS au - ON sd.SupervisorAdminID = au.AdminID - INNER JOIN Centres AS ct - ON au.CentreID = ct.CentreID - ON da.CentreID = ct.CentreID - AND da.UserID = sd.DelegateUserID - LEFT OUTER JOIN Users AS u - LEFT OUTER JOIN JobGroups AS jg - ON u.JobGroupID = jg.JobGroupID - ON da.UserID = u.ID - LEFT OUTER JOIN CustomPrompts AS cp1 - ON ct.CustomField1PromptID = cp1.CustomPromptID - LEFT OUTER JOIN CustomPrompts AS cp2 - ON ct.CustomField2PromptID = cp2.CustomPromptID - LEFT OUTER JOIN CustomPrompts AS cp3 - ON ct.CustomField3PromptID = cp3.CustomPromptID - LEFT OUTER JOIN CustomPrompts AS cp4 - ON ct.CustomField4PromptID = cp4.CustomPromptID - ON cp5.CustomPromptID = ct.CustomField5PromptID - ON cp6.CustomPromptID = ct.CustomField6PromptID - LEFT OUTER JOIN AdminAccounts AS au2 - ON da.UserID = au2.UserID AND da.CentreID = au2.CentreID - WHERE (sd.SupervisorAdminID = @adminId) AND (sd.Removed IS NULL) AND - (u.ID = da.UserID OR sd.DelegateUserID IS NULL)) AS StaffCount, - (SELECT COUNT(ID) AS StaffCount - FROM SupervisorDelegates AS SupervisorDelegates_1 - WHERE (SupervisorAdminID = @adminId) - AND (DelegateUserID IS NULL) - AND (Removed IS NULL)) AS StaffUnregisteredCount, - (SELECT COUNT(ca.ID) AS ProfileSelfAssessmentCount - FROM CandidateAssessmentSupervisors AS cas INNER JOIN - CandidateAssessments AS ca ON cas.CandidateAssessmentID = ca.ID INNER JOIN - SupervisorDelegates AS sd ON cas.SupervisorDelegateId = sd.ID - WHERE (sd.SupervisorAdminID = @adminId) AND (cas.Removed IS NULL) AND ((ca.RemovedDate IS NULL))) AS ProfileSelfAssessmentCount, - (SELECT COUNT(DISTINCT sa.ID) AS Expr1 - FROM SelfAssessments AS sa INNER JOIN - CandidateAssessments AS ca ON sa.ID = ca.SelfAssessmentID LEFT OUTER JOIN - SupervisorDelegates AS sd INNER JOIN - CandidateAssessmentSupervisors AS cas ON sd.ID = cas.SupervisorDelegateId ON ca.ID = cas.CandidateAssessmentID - WHERE (sd.SupervisorAdminID = @adminId)) As ProfileCount, - COALESCE - ((SELECT COUNT(casv.ID) AS Expr1 - FROM CandidateAssessmentSupervisors AS cas INNER JOIN - CandidateAssessments AS ca ON cas.CandidateAssessmentID = ca.ID INNER JOIN - SupervisorDelegates AS sd ON cas.SupervisorDelegateId = sd.ID INNER JOIN - CandidateAssessmentSupervisorVerifications AS casv ON cas.ID = casv.CandidateAssessmentSupervisorID - WHERE (sd.SupervisorAdminID = @adminId) AND ((ca.RemovedDate IS NULL) AND (cas.Removed IS NULL)) AND (casv.Verified IS NULL) - ), 0) AS AwaitingReviewCount", new { adminId } + @"WITH BaseDelegates AS ( + SELECT ID,DelegateUserID + FROM SupervisorDelegates + WHERE SupervisorAdminID = @adminId + AND Removed IS NULL + ), + StaffCounts AS ( + SELECT + COUNT(*) AS StaffCount, + SUM(CASE WHEN DelegateUserID IS NULL THEN 1 ELSE 0 END) AS StaffUnregisteredCount + FROM BaseDelegates + ), + ProfileSelfAssessments AS ( + SELECT COUNT(ca.ID) AS ProfileSelfAssessmentCount + FROM CandidateAssessmentSupervisors cas + INNER JOIN CandidateAssessments ca + ON cas.CandidateAssessmentID = ca.ID + INNER JOIN SupervisorDelegates sd + ON cas.SupervisorDelegateId = sd.ID + WHERE cas.Removed IS NULL + AND ca.RemovedDate IS NULL + AND sd.SupervisorAdminID = 10857 + ), + ProfileCounts AS ( + SELECT COUNT(DISTINCT sa.ID) AS ProfileCount + FROM SelfAssessments sa + JOIN CandidateAssessments ca + ON sa.ID = ca.SelfAssessmentID + JOIN CandidateAssessmentSupervisors cas + ON ca.ID = cas.CandidateAssessmentID + JOIN BaseDelegates sd + ON cas.SupervisorDelegateId = sd.ID + ), + AwaitingReviews AS ( + SELECT COUNT(casv.ID) AS AwaitingReviewCount + FROM CandidateAssessmentSupervisors cas + JOIN CandidateAssessments ca + ON cas.CandidateAssessmentID = ca.ID + JOIN BaseDelegates sd + ON cas.SupervisorDelegateId = sd.ID + JOIN CandidateAssessmentSupervisorVerifications casv + ON cas.ID = casv.CandidateAssessmentSupervisorID + WHERE cas.Removed IS NULL + AND ca.RemovedDate IS NULL + AND casv.Verified IS NULL + ) + + SELECT + sc.StaffCount, + sc.StaffUnregisteredCount, + psa.ProfileSelfAssessmentCount, + pc.ProfileCount, + COALESCE(ar.AwaitingReviewCount, 0) AS AwaitingReviewCount + FROM StaffCounts sc + CROSS JOIN ProfileSelfAssessments psa + CROSS JOIN ProfileCounts pc + CROSS JOIN AwaitingReviews ar;", new { adminId } ).FirstOrDefault(); } @@ -522,22 +542,6 @@ FROM AdminAccounts AS aa INNER JOIN new { CentreID, CategoryID }); } - public bool ConfirmSupervisorDelegateById(int supervisorDelegateId, int delegateUserId, int adminId) - { - var numberOfAffectedRows = connection.Execute( - @"UPDATE SupervisorDelegates SET Confirmed = getUTCDate() - WHERE ID = @supervisorDelegateId AND Confirmed IS NULL AND Removed IS NULL AND (CandidateID = @candidateId OR SupervisorAdminID = @adminId)", - new { supervisorDelegateId, delegateUserId, adminId }); - if (numberOfAffectedRows < 1) - { - logger.LogWarning( - $"Not confirming SupervisorDelegate as db update failed. supervisorDelegateId: {supervisorDelegateId}, delegateUserId: {delegateUserId}, adminId: {adminId}" - ); - return false; - } - return true; - } - public bool RemoveSupervisorDelegateById(int supervisorDelegateId, int delegateUserId, int adminId) { @@ -680,23 +684,43 @@ public IEnumerable GetSupervisorDashboardToDoItemsF public IEnumerable GetSupervisorDashboardToDoItemsForRequestedReviews(int adminId) { return connection.Query( - @"SELECT ca.ID, sd.ID AS SupervisorDelegateId, u.FirstName + ' ' + u.LastName AS DelegateName, sa.Name AS ProfileName, MAX(sasv.Requested) AS Requested, 0 AS SignOffRequest, 1 AS ResultsReviewRequest FROM CandidateAssessmentSupervisors AS cas INNER JOIN - CandidateAssessments AS ca ON cas.CandidateAssessmentID = ca.ID INNER JOIN - Users AS u ON ca.DelegateUserID = u.ID INNER JOIN - SelfAssessments AS sa ON ca.SelfAssessmentID = sa.ID INNER JOIN - SupervisorDelegates AS sd ON cas.SupervisorDelegateId = sd.ID INNER JOIN - SelfAssessmentResults AS sar ON sar.SelfAssessmentID = sa.ID INNER JOIN - Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN - SelfAssessmentResultSupervisorVerifications AS sasv ON sasv.SelfAssessmentResultId = sar.ID AND sasv.Superceded = 0 - AND sasv.CandidateAssessmentSupervisorID = cas.ID AND sar.DateTime = ( - SELECT TOP 1 sar2.DateTime - FROM SelfAssessmentResults AS sar2 - WHERE sar2.ID = sar.ID AND sar2.SelfAssessmentID = sar.SelfAssessmentID AND sar2.CompetencyID = co.ID AND sar2.Result != 0 ORDER BY sar2.ID DESC - ) INNER JOIN - AdminAccounts AS aa ON sd.SupervisorAdminID = aa.ID - WHERE (sd.SupervisorAdminID = @adminId) AND (cas.Removed IS NULL) AND (sasv.Verified IS NULL) AND (sd.Removed IS NULL) - AND (aa.CategoryID is null or sa.CategoryID = aa.CategoryID) - GROUP BY sa.ID, ca.ID, sd.ID, u.FirstName, u.LastName, sa.Name", new { adminId } + @"WITH FilteredSelfAssessmentResults AS ( + SELECT + sar.ID, + sar.SelfAssessmentID, + sar.CompetencyID, + sar.DateTime + FROM SelfAssessmentResults sar + WHERE sar.Result <> 0 + ), + BaseSupervisorDelegates AS ( + SELECT ID, SupervisorAdminID + FROM SupervisorDelegates + WHERE SupervisorAdminID = @adminId + AND Removed IS NULL + ), + ValidCAS AS ( + SELECT ID, CandidateAssessmentID, SupervisorDelegateId + FROM CandidateAssessmentSupervisors + WHERE Removed IS NULL + ) + SELECT ca.ID,sd.ID AS SupervisorDelegateId, u.FirstName + ' ' + u.LastName AS DelegateName, + sa.Name AS ProfileName, MAX(sasv.Requested) AS Requested, 0 AS SignOffRequest, 1 AS ResultsReviewRequest + FROM ValidCAS cas + INNER JOIN CandidateAssessments ca + ON cas.CandidateAssessmentID = ca.ID + INNER JOIN Users u + ON ca.DelegateUserID = u.ID + INNER JOIN SelfAssessments sa ON ca.SelfAssessmentID = sa.ID + INNER JOIN BaseSupervisorDelegates sd ON cas.SupervisorDelegateId = sd.ID + INNER JOIN FilteredSelfAssessmentResults sar ON sar.SelfAssessmentID = sa.ID + INNER JOIN Competencies co ON sar.CompetencyID = co.ID + INNER JOIN SelfAssessmentResultSupervisorVerifications sasv + ON sasv.SelfAssessmentResultId = sar.ID AND sasv.Superceded = 0 AND sasv.CandidateAssessmentSupervisorID = cas.ID + INNER JOIN AdminAccounts aa ON sd.SupervisorAdminID = aa.ID + WHERE sasv.Verified IS NULL AND ca.RemovedDate IS NULL + AND (aa.CategoryID IS NULL OR sa.CategoryID = aa.CategoryID) + GROUP BY sa.ID, ca.ID, sd.ID, u.FirstName, u.LastName, sa.Name;", new { adminId } ); } @@ -772,10 +796,10 @@ public int RemoveSelfAssessmentResultSupervisorVerificationById(int id) } return numberOfAffectedRows; } - public IEnumerable GetAvailableRoleProfilesForDelegate(int delegateUserId, int centreId, int? categoryId) + public IEnumerable GetAvailableCompetencyAssessmentsForDelegate(int delegateUserId, int centreId, int? categoryId) { - return connection.Query( - $@"SELECT rp.ID, rp.Name AS RoleProfileName, rp.Description, rp.BrandID, rp.ParentSelfAssessmentID, rp.[National], rp.[Public], rp.CreatedByAdminID AS OwnerAdminID, rp.NRPProfessionalGroupID, rp.NRPSubGroupID, rp.NRPRoleID, rp.PublishStatusID, 0 AS UserRole, rp.CreatedDate, + return connection.Query( + $@"SELECT rp.ID, rp.Name AS CompetencyAssessmentName, rp.Description, rp.BrandID, rp.ParentSelfAssessmentID, rp.[National], rp.[Public], rp.CreatedByAdminID AS OwnerAdminID, rp.NRPProfessionalGroupID, rp.NRPSubGroupID, rp.NRPRoleID, rp.PublishStatusID, 0 AS UserRole, rp.CreatedDate, (SELECT BrandName FROM Brands WHERE (BrandID = rp.BrandID)) AS Brand, '' AS ParentSelfAssessment, '' AS Owner, rp.Archived, rp.LastEdit, @@ -801,10 +825,10 @@ FROM CandidateAssessments AS CA ); } - public RoleProfile? GetRoleProfileById(int selfAssessmentId) + public CompetencyAssessment? GetCompetencyAssessmentById(int selfAssessmentId) { - return connection.Query( - $@"SELECT ID, Name AS RoleProfileName, Description, BrandID, ParentSelfAssessmentID, [National], [Public], CreatedByAdminID AS OwnerAdminID, NRPProfessionalGroupID, NRPSubGroupID, NRPRoleID, PublishStatusID, 0 AS UserRole, CreatedDate, + return connection.Query( + $@"SELECT ID, Name AS CompetencyAssessmentName, Description, BrandID, ParentSelfAssessmentID, [National], [Public], CreatedByAdminID AS OwnerAdminID, NRPProfessionalGroupID, NRPSubGroupID, NRPRoleID, PublishStatusID, 0 AS UserRole, CreatedDate, (SELECT BrandName FROM Brands WHERE (BrandID = rp.BrandID)) AS Brand, @@ -839,7 +863,7 @@ public IEnumerable GetSupervisorRolesBySelfAssessm return connection.Query( $@"SELECT ID, SelfAssessmentID, RoleName, RoleDescription, SelfAssessmentReview, ResultsReview FROM SelfAssessmentSupervisorRoles - WHERE (SelfAssessmentID = @selfAssessmentId) AND (AllowSupervisorRoleSelection = 1) + WHERE (SelfAssessmentID = @selfAssessmentId) AND (AllowSupervisorRoleSelection = 1) ORDER BY RoleName", new { selfAssessmentId } ); } @@ -893,14 +917,14 @@ FROM CandidateAssessments @"UPDATE CandidateAssessments SET DelegateUserID = @delegateUserId, SelfAssessmentID = @selfAssessmentId, - CompleteByDate = NULL, + CompleteByDate = @completeByDate, EnrolmentMethodId = 2, EnrolledByAdminId = @adminId, CentreID = @centreId, RemovedDate = NULL, NonReportable = CASE WHEN NonReportable = 1 THEN NonReportable ELSE @isLoggedInUser END WHERE ID = @existingCandidateAssessmentId", - new { delegateUserId, selfAssessmentId, adminId, centreId, existingCandidateAssessmentId, isLoggedInUser }); + new { delegateUserId, selfAssessmentId, adminId, centreId, existingCandidateAssessmentId, isLoggedInUser, completeByDate }); if (numberOfAffectedRows < 1) { diff --git a/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs b/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs index a0d60c8399..7691f565e9 100644 --- a/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs +++ b/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs @@ -91,9 +91,9 @@ public class NavMenuTab : Enumeration nameof(Frameworks) ); - public static readonly NavMenuTab RolesProfiles = new NavMenuTab( + public static readonly NavMenuTab CompetencyAssessments = new NavMenuTab( 17, - nameof(RolesProfiles) + nameof(CompetencyAssessments) ); public static readonly NavMenuTab LogOut = new NavMenuTab( diff --git a/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs b/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs index f869df5f74..61dc157340 100644 --- a/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs +++ b/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs @@ -60,6 +60,8 @@ public static class ConfigurationExtensions private const string TableauViewName = "ViewName"; private const string TableauSiteName = "SiteName"; private const string TableauAuthApi = "AuthApiPath"; + private const string SupervisorDefaultText = "SupervisorDefaultText"; + private const string LearnerDefaultText = "LearnerDefaultText"; public static string GetAppRootPath(this IConfiguration config) { return config[AppRootPathName]!; @@ -268,5 +270,13 @@ public static string GetTableauViewName(this IConfiguration config) { return config[$"{TableauSectionKey}:{TableauViewName}"]!; } + public static string GetSupervisorDefaultText(this IConfiguration config) + { + return config[SupervisorDefaultText]!; + } + public static string GetLearnerDefaultText(this IConfiguration config) + { + return config[LearnerDefaultText]!; + } } } diff --git a/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs b/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs index aca9575264..8cb1d20ee0 100644 --- a/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs +++ b/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs @@ -6,6 +6,7 @@ namespace DigitalLearningSolutions.Data.Models.Common.Users public class Administrator : BaseSearchableItem { public int AdminID { get; set; } + public int AdminUserID { get; set; } public int CentreID { get; set; } public string? Email { get; set; } public string? Forename { get; set; } diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/Competency.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/Competency.cs new file mode 100644 index 0000000000..0640c403cd --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/Competency.cs @@ -0,0 +1,20 @@ +using DigitalLearningSolutions.Data.Models.Frameworks; +using System.Collections.Generic; + +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class Competency + { + public int StructureId { get; set; } + public int CompetencyID { get; set; } + public int FrameworkId { get; set; } + public string? FrameworkName { get; set; } + public string? GroupName { get; set; } + public int GroupId { get; set; } + public string? CompetencyName { get; set; } + public string? CompetencyDescription { get; set; } + public bool Optional { get; set; } + public bool GroupOptionalCompetencies { get; set; } + public IEnumerable CompetencyFlags { get; set; } = []; + } +} diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfile.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessment.cs similarity index 53% rename from DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfile.cs rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessment.cs index ec1970fc92..b8c2fe8b2a 100644 --- a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfile.cs +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessment.cs @@ -1,8 +1,7 @@ -namespace DigitalLearningSolutions.Data.Models.RoleProfiles +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments { using System; - using System.ComponentModel.DataAnnotations; - public class RoleProfile : RoleProfileBase + public class CompetencyAssessment : CompetencyAssessmentBase { public DateTime CreatedDate { get; set; } public string? Brand @@ -10,15 +9,27 @@ public string? Brand get => brand; set => brand = GetValidOrNull(value); } - public string? ParentRoleProfile { get; set; } + public string? Category + { + get => category; + set => category = GetValidOrNull(value); + } + public string? ParentCompetencyAssessment { get; set; } public string? Owner { get; set; } public DateTime? Archived { get; set; } public DateTime LastEdit { get; set; } + public string LinkedFrameworks + { + get => linkedFrameworks ?? "None"; + set => linkedFrameworks = value; + } public string? NRPProfessionalGroup { get; set; } public string? NRPSubGroup { get; set; } public string? NRPRole { get; set; } - public int? RoleProfileReviewID { get; set; } + public int? CompetencyAssessmentReviewID { get; set; } private string? brand; + private string? category; + private string? linkedFrameworks; private static string? GetValidOrNull(string? toValidate) { return toValidate != null && toValidate.ToLower() == "undefined" ? null : toValidate; diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentBase.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentBase.cs new file mode 100644 index 0000000000..58a11866d7 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentBase.cs @@ -0,0 +1,41 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + using System.ComponentModel.DataAnnotations; + public class CompetencyAssessmentBase + { + public int ID { get; set; } + [StringLength(255, MinimumLength = 3)] + [Required] + public string CompetencyAssessmentName { get; set; } = string.Empty; + public string? Description { get; set; } + public int BrandID { get; set; } + public int CategoryID { get; set; } + public int? ParentCompetencyAssessmentID { get; set; } + public bool National { get; set; } + public bool Public { get; set; } + public int OwnerAdminID { get; set; } + public int? NRPProfessionalGroupID { get; set; } + public int? NRPSubGroupID { get; set; } + public int? NRPRoleID { get; set; } + public int PublishStatusID { get; set; } + public int UserRole { get; set; } + public string? Vocabulary { get; set; } + public bool EnforceRoleRequirementsForSignOff { get; set; } + public bool IncludeRequirementsFilters { get; set; } + public int? MinimumOptionalCompetencies { get; set; } + public string? ManageOptionalCompetenciesPrompt { get; set; } + public bool IncludeLearnerDeclarationPrompt { get; set; } + public bool IncludesSignposting { get; set; } + public bool LinearNavigation { get; set; } + public bool UseDescriptionExpanders { get; set; } + public string? QuestionLabel { get; set; } + public string? ReviewerCommentsLabel { get; set; } + public bool SupervisorSelfAssessmentReview { get; set; } + public bool SupervisorResultsReview { get; set; } + public string? SignOffRequestorStatement { get; set; } + public string? SignOffSupervisorStatement { get; set; } + public int? SelfAssessmentReviewID { get; set; } + public int? SelfAssessmentCommentID { get; set; } + } +} + diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaborator.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaborator.cs new file mode 100644 index 0000000000..9e58b3199e --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaborator.cs @@ -0,0 +1,10 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class CompetencyAssessmentCollaborator + { + public int ID { get; set; } + public int SelfAssessmentID { get; set; } + public int? AdminID { get; set; } + public bool CanModify { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorDetail.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorDetail.cs new file mode 100644 index 0000000000..35a88d3bbc --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorDetail.cs @@ -0,0 +1,11 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class CompetencyAssessmentCollaboratorDetail : CompetencyAssessmentCollaborator + { + public string? UserEmail { get; set; } + public bool? UserActive { get; set; } + public string? CompetencyAssessmentRole { get; set; } + public int? SelfAssessmentReviewID { get; set; } + public bool SignOffRequired { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorNotification.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorNotification.cs new file mode 100644 index 0000000000..f1578157cf --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorNotification.cs @@ -0,0 +1,9 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class CompetencyAssessmentCollaboratorNotification : CompetencyAssessmentCollaboratorDetail + { + public string InvitedByEmail { get; set; } = string.Empty; + public string InvitedByName { get; set; } = string.Empty; + public string CompetencyAssessmentName { get; set; } = string.Empty; + } +} diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentFeatures.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentFeatures.cs new file mode 100644 index 0000000000..f68bfe7a67 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentFeatures.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class CompetencyAssessmentFeatures + { + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + public int UserRole { get; set; } + public bool DescriptionStatus { get; set; } + public bool ProviderandCategoryStatus { get; set; } + public bool VocabularyStatus { get; set; } + public bool WorkingGroupStatus { get; set; } + public bool AllframeworkCompetenciesStatus { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentTaskStatus.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentTaskStatus.cs new file mode 100644 index 0000000000..654220f151 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentTaskStatus.cs @@ -0,0 +1,20 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class CompetencyAssessmentTaskStatus + { + public int Id { get; set; } + public bool? IntroductoryTextTaskStatus { get; set; } + public bool? BrandingTaskStatus { get; set; } + public bool? VocabularyTaskStatus { get; set; } + public bool? WorkingGroupTaskStatus { get; set; } + public bool? NationalRoleProfileTaskStatus { get; set; } + public bool? FrameworkLinksTaskStatus { get; set; } + public bool? SelectCompetenciesTaskStatus { get; set; } + public bool? OptionalCompetenciesTaskStatus { get; set; } + public bool? RoleRequirementsTaskStatus { get; set; } + public bool? SupervisorRolesTaskStatus { get; set; } + public bool? SelfAssessmentOptionsTaskStatus { get; set; } + public bool? ReviewTaskStatus { get; set; } + + } +} diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyWithAssessmentQuestionRoleRequirements.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyWithAssessmentQuestionRoleRequirements.cs new file mode 100644 index 0000000000..0cf1dcedc1 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyWithAssessmentQuestionRoleRequirements.cs @@ -0,0 +1,19 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class CompetencyWithAssessmentQuestionRoleRequirements + { + public int? CompetencyGroupID { get; set; } + public string? GroupName { get; set; } + public int CompetencyID { get; set; } + public string? Competency { get; set; } + public string? CompetencyDescription { get; set; } + public bool? Optional { get; set; } + public int AssessmentQuestionID { get; set; } + public string? Question { get; set; } + public string? InputTypeName { get; set; } + public bool Required { get; set; } + public int? ResponseValue { get; set; } + public string? Response { get; set; } + public int? LevelRAG { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/LinkedFrameworks.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/LinkedFrameworks.cs new file mode 100644 index 0000000000..f410bab903 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/LinkedFrameworks.cs @@ -0,0 +1,9 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.Frameworks; + public class LinkedFramework : BaseFramework + { + public int AssessmentFrameworkCompetencyCount { get; set; } = 0; + public bool IsPrimary { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPProfessionalGroups.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPProfessionalGroups.cs similarity index 81% rename from DigitalLearningSolutions.Data/Models/RoleProfiles/NRPProfessionalGroups.cs rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPProfessionalGroups.cs index de1f9e4588..bdc4f6942a 100644 --- a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPProfessionalGroups.cs +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPProfessionalGroups.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Data.Models.RoleProfiles +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments { using System.ComponentModel.DataAnnotations; public class NRPProfessionalGroups @@ -10,3 +10,4 @@ public class NRPProfessionalGroups public bool Active { get; set; } } } + diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPRoles.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPRoles.cs similarity index 67% rename from DigitalLearningSolutions.Data/Models/RoleProfiles/NRPRoles.cs rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPRoles.cs index f404bfb243..22cba2ada8 100644 --- a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPRoles.cs +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPRoles.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Data.Models.RoleProfiles +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments { using System.ComponentModel.DataAnnotations; public class NRPRoles @@ -7,7 +7,7 @@ public class NRPRoles public int NRPSubGroupID { get; set; } [StringLength(255, MinimumLength = 3)] [Required] - public string RoleProfile { get; set; } = string.Empty; + public string ProfileName { get; set; } = string.Empty; public bool Active { get; set; } } } diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPSubGroups.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPSubGroups.cs similarity index 71% rename from DigitalLearningSolutions.Data/Models/RoleProfiles/NRPSubGroups.cs rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPSubGroups.cs index 6d43b936a2..f100a6b2e2 100644 --- a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPSubGroups.cs +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPSubGroups.cs @@ -1,10 +1,9 @@ -namespace DigitalLearningSolutions.Data.Models.RoleProfiles +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments { using System.ComponentModel.DataAnnotations; public class NRPSubGroups { public int ID { get; set; } - public int NRPProfessionalGroupID { get; set; } [StringLength(255, MinimumLength = 3)] [Required] public string SubGroup { get; set; } = string.Empty; diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReview.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReview.cs new file mode 100644 index 0000000000..9d9919b0d1 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReview.cs @@ -0,0 +1,18 @@ +using System; +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class SelfAssessmentReview + { + public int ID { get; set; } + public int SelfAssessmentID { get; set; } + public int SelfAssessmentCollaboratorID { get; set; } + public string UserEmail { get; set; } = string.Empty; + public bool IsRegistered { get; set; } + public DateTime ReviewRequested { get; set; } + public DateTime? ReviewComplete { get; set; } + public bool SignedOff { get; set; } + public int? SelfAssessmentCommentID { get; set; } + public string? Comment { get; set; } + public bool SignOffRequired { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReviewOutcomeNotification.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReviewOutcomeNotification.cs new file mode 100644 index 0000000000..7babb86aaf --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReviewOutcomeNotification.cs @@ -0,0 +1,12 @@ +namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments +{ + public class SelfAssessmentReviewOutcomeNotification : SelfAssessmentReview + { + public string SelfAssessmentName { get; set; } = string.Empty; + public string? OwnerEmail { get; set; } + public string? OwnerFirstName { get; set; } + public string? ReviewerFirstName { get; set; } + public string? ReviewerLastName { get; set; } + public bool? ReviewerActive { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs b/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs index 0291d3d13f..38adaf1cb8 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs @@ -22,7 +22,6 @@ public class BaseFramework : BaseSearchableItem public string? UpdatedBy { get; set; } public int UserRole { get; set; } public int? FrameworkReviewID { get; set; } - public override string SearchableName { get => SearchableNameOverrideForFuzzySharp ?? FrameworkName; diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs b/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs index f49b526831..5ba38617ac 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs @@ -2,6 +2,8 @@ { public class BrandedFramework : BaseFramework { + public string? Description { get; set; } + public string? Vocabulary { get; set; } public string? Brand { get => brand; diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs index f5d28bd284..207e61079c 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs @@ -4,7 +4,7 @@ public class DashboardData { public int FrameworksCount { get; set; } public int MyFrameworksCount { get; set; } - public int RoleProfileCount { get; set; } - public int MyRoleProfileCount { get; set; } + public int CompetencyAssessmentCount { get; set; } + public int MyCompetencyAssessmentCount { get; set; } } } diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs index 96ee4e8326..dc7e6b0386 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs @@ -4,7 +4,7 @@ public class DashboardToDoItem { public int? FrameworkID { get; set; } - public int? RoleProfileID { get; set; } + public int? CompetencyAssessmentID { get; set; } public string ItemName { get; set; } = string.Empty; public string RequestorName { get; set; } = string.Empty; public bool SignOffRequired { get; set; } diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs b/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs index a42d3d10fe..cf2117457c 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs @@ -1,6 +1,7 @@ namespace DigitalLearningSolutions.Data.Models.Frameworks { using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate; + using System.Collections.Generic; using System.ComponentModel.DataAnnotations; public class FrameworkCompetency : BaseSearchableItem { @@ -15,6 +16,7 @@ public class FrameworkCompetency : BaseSearchableItem public int CompetencyLearningResourcesCount { get; set; } public string? FrameworkName { get; set; } public bool? AlwaysShowDescription { get; set; } + public IEnumerable CompetencyFlags { get; set; } = new List(); public override string SearchableName { diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfileBase.cs b/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfileBase.cs deleted file mode 100644 index 1c843fae46..0000000000 --- a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfileBase.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace DigitalLearningSolutions.Data.Models.RoleProfiles -{ - using System; - using System.ComponentModel.DataAnnotations; - public class RoleProfileBase - { - public int ID { get; set; } - [StringLength(255, MinimumLength = 3)] - [Required] - public string RoleProfileName { get; set; } = string.Empty; - public string? Description { get; set; } - public int BrandID { get; set; } - public int? ParentRoleProfileID { get; set; } - public bool National { get; set; } - public bool Public { get; set; } - public int OwnerAdminID { get; set; } - public int? NRPProfessionalGroupID { get; set; } - public int? NRPSubGroupID { get; set; } - public int? NRPRoleID { get; set; } - public int PublishStatusID { get; set; } - public int UserRole { get; set; } - - } -} diff --git a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs index dbd1da0de6..4fcf6363e2 100644 --- a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs +++ b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs @@ -17,6 +17,7 @@ public class SelfAssessment : CurrentLearningItem public DateTime? EnrolmentCutoffDate { get; set; } public string? RetirementReason { get; set; } + public bool IncludeLearnerDeclarationPrompt { get; set; } } } diff --git a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs index 89ef4e61b5..1814e544d7 100644 --- a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs +++ b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs @@ -9,6 +9,7 @@ public class SelfAssessmentSupervisor public int ID { get; set; } public int SupervisorDelegateID { get; set; } public int? SupervisorAdminID { get; set; } + public int? SupervisorAdminUserID { get; set; } public string SupervisorName { get; set; } = string.Empty; public string SupervisorEmail { get; set; } = string.Empty; public DateTime NotificationSent { get; set; } @@ -20,5 +21,5 @@ public class SelfAssessmentSupervisor public string CentreName { get; set; } = string.Empty; public bool AllowDelegateNomination { get; set; } public bool AllowSupervisorRoleSelection { get; set; } -} + } } diff --git a/DigitalLearningSolutions.Data/Models/SessionData/CompetencyAssessments/SessionNewCompetencyAssessment.cs b/DigitalLearningSolutions.Data/Models/SessionData/CompetencyAssessments/SessionNewCompetencyAssessment.cs new file mode 100644 index 0000000000..b8d572404f --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/SessionData/CompetencyAssessments/SessionNewCompetencyAssessment.cs @@ -0,0 +1,15 @@ +namespace DigitalLearningSolutions.Data.Models.SessionData.CompetencyAssessments +{ + using System; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + public class SessionNewCompetencyAssessment + { + public SessionNewCompetencyAssessment() + { + Id = new Guid(); + CompetencyAssessmentBase = new CompetencyAssessmentBase(); + } + public Guid Id { get; set; } + public CompetencyAssessmentBase CompetencyAssessmentBase { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/SessionData/RoleProfiles/SessionNewRoleProfile.cs b/DigitalLearningSolutions.Data/Models/SessionData/RoleProfiles/SessionNewRoleProfile.cs deleted file mode 100644 index 617c8ebb64..0000000000 --- a/DigitalLearningSolutions.Data/Models/SessionData/RoleProfiles/SessionNewRoleProfile.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace DigitalLearningSolutions.Data.Models.SessionData.RoleProfiles -{ - using System; - using DigitalLearningSolutions.Data.Models.RoleProfiles; - public class SessionNewRoleProfile - { - public SessionNewRoleProfile() - { - Id = new Guid(); - RoleProfileBase = new RoleProfileBase(); - } - public Guid Id { get; set; } - public RoleProfileBase RoleProfileBase { get; set; } - } -} diff --git a/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs b/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs index af9629f071..398a55d3d0 100644 --- a/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs +++ b/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs @@ -8,5 +8,6 @@ public class SessionAddSupervisor public string? SupervisorEmail { get; set; } public int? SelfAssessmentSupervisorRoleId { get; set; } public int? CentreID { get; set; } + public bool NonReportable { get; set; } } } diff --git a/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnRoleProfile.cs b/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnCompetencyAssessment.cs similarity index 86% rename from DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnRoleProfile.cs rename to DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnCompetencyAssessment.cs index 6d2f3e777a..a3271d569d 100644 --- a/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnRoleProfile.cs +++ b/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnCompetencyAssessment.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Data.Models.SessionData.Supervisor { using System; - public class SessionEnrolOnRoleProfile + public class SessionEnrolOnCompetencyAssessment { public int? SelfAssessmentID { get; set; } public DateTime? CompleteByDate { get; set; } diff --git a/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs b/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs index 69da5a36fd..212089f305 100644 --- a/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs +++ b/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs @@ -22,7 +22,7 @@ public class DelegateSelfAssessment public string? DescriptionLabel { get; set; } public string? ReviewerCommentsLabel { get; set; } public string? SubGroup { get; set; } - public string? RoleProfile { get; set; } + public string? CompetencyAssessment { get; set; } public int SignOffRequested { get; set; } public int ResultsVerificationRequests { get; set; } public bool IsSupervisorResultsReviewed { get; set; } diff --git a/DigitalLearningSolutions.Data/Models/User/DelegateUserCard.cs b/DigitalLearningSolutions.Data/Models/User/DelegateUserCard.cs index 1e9caf4e9d..7e0674e279 100644 --- a/DigitalLearningSolutions.Data/Models/User/DelegateUserCard.cs +++ b/DigitalLearningSolutions.Data/Models/User/DelegateUserCard.cs @@ -20,6 +20,7 @@ public DelegateUserCard(DelegateEntity delegateEntity) FirstName = delegateEntity.UserAccount.FirstName; LastName = delegateEntity.UserAccount.LastName; EmailAddress = delegateEntity.UserCentreDetails?.Email ?? delegateEntity.UserAccount.PrimaryEmail; + PrimaryEmail = Guid.TryParse(delegateEntity.UserAccount.PrimaryEmail, out _) ? null : delegateEntity.UserAccount.PrimaryEmail; Password = delegateEntity.UserAccount.PasswordHash; CandidateNumber = delegateEntity.DelegateAccount.CandidateNumber; DateRegistered = delegateEntity.DelegateAccount.DateRegistered; @@ -48,7 +49,8 @@ public DelegateUserCard(DelegateEntity delegateEntity) public bool IsAdmin => AdminId.HasValue; public bool IsYetToBeClaimed => RegistrationConfirmationHash != null; public bool IsEmailVerified => EmailVerified != null; - + public string? Email { get; set; } + public string? PrimaryEmail { get; set; } public RegistrationType RegistrationType => (SelfReg, ExternalReg) switch { (true, true) => RegistrationType.SelfRegisteredExternal, diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs index 0d4a77fc61..0040c2b514 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs @@ -176,7 +176,7 @@ public void LearnerInformationPost_updates_tempdata_correctly() const string answer4 = "answer4"; const string answer5 = "answer5"; const string answer6 = "answer6"; - const string professionalRegistrationNumber = "PRN1234"; + const string professionalRegistrationNumber = "PR123456"; var model = new LearnerInformationViewModel { JobGroup = jobGroupId, diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs index 7dddbd0e55..0be1c1118c 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs @@ -211,7 +211,7 @@ public void ReviewDelegateSelfAssessment_Should_Return_View_With_Optional_Compet result.Should().BeViewResult() .WithViewName("ReviewSelfAssessment") .ModelAs() - .CompetencyGroups ?.SelectMany(group => group).FirstOrDefault(x => x.Id == 1)?.Optional.Should().Be(true); + .CompetencyGroups?.SelectMany(group => group).FirstOrDefault(x => x.Id == 1)?.Optional.Should().Be(true); result.Should().BeViewResult() .WithViewName("ReviewSelfAssessment") .ModelAs() diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs index 5c5df8ac44..c08f3dbe35 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs @@ -175,7 +175,7 @@ public void Index_post_returns_view_with_model_error_with_invalid_prn() result.As().Model.Should().BeOfType(); AssertModelStateErrorIsExpected( result, - "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed" + ErrorMessagesTestHelper.InvalidFormatError ); A.CallTo(() => userService.GetDelegateById(A._)).MustNotHaveHappened(); } diff --git a/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs b/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs index d78c74f042..a7270f280d 100644 --- a/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs @@ -1,11 +1,12 @@ namespace DigitalLearningSolutions.Web.Tests.Helpers { - using System.Linq; using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.Tests.TestHelpers; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.AspNetCore.Mvc.ModelBinding; using NUnit.Framework; + using System.Linq; public class ProfessionalRegistrationNumberHelperTests { @@ -70,7 +71,7 @@ public void ValidateProfessionalRegistrationNumber_does_not_set_errors_when_vali { // Given var state = new ModelStateDictionary(); - const string validPrn = "abc-123"; + const string validPrn = "AB123456"; // When ProfessionalRegistrationNumberHelper.ValidateProfessionalRegistrationNumber( @@ -104,22 +105,13 @@ public void ValidateProfessionalRegistrationNumber_sets_error_when_hasPrn_is_not } } - [TestCase(null, "Enter a professional registration number")] - [TestCase("", "Enter a professional registration number")] - [TestCase("123", "Professional registration number must be between 5 and 20 characters")] - [TestCase("0123456789-0123456789", "Professional registration number must be between 5 and 20 characters")] - [TestCase( - "01234_", - "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed" - )] - [TestCase( - "01234 ", - "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed" - )] - [TestCase( - "01234$", - "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed" - )] + [TestCase(null, ErrorMessagesTestHelper.MissingNumberError)] + [TestCase("", ErrorMessagesTestHelper.MissingNumberError)] + [TestCase("1234", ErrorMessagesTestHelper.LengthError)] + [TestCase("1234", ErrorMessagesTestHelper.LengthError)] + [TestCase("01234_", ErrorMessagesTestHelper.InvalidFormatError)] + [TestCase("01234 ", ErrorMessagesTestHelper.InvalidFormatError)] + [TestCase("01234$", ErrorMessagesTestHelper.InvalidFormatError)] public void ValidateProfessionalRegistrationNumber_sets_error_when_prn_is_invalid( string prn, string expectedError diff --git a/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs b/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs index fa333c2d7a..9f2f83dde4 100644 --- a/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs @@ -111,7 +111,7 @@ public void EnrolDelegateOnGroupCourses_adds_new_progress_record_when_existing_p } [Test] - public void EnrolDelegateOnGroupCourses_adds_new_progress_record_when_existing_progress_record_is_completed() + public void EnrolDelegateOnGroupCourses_does_not_create_new_progress_record_when_existing_progress_record_is_completed() { // Given var existingProgressRecord = ProgressTestHelper.GetDefaultProgress(completed: testDate); @@ -135,22 +135,25 @@ public void EnrolDelegateOnGroupCourses_adds_new_progress_record_when_existing_p using (new AssertionScope()) { DelegateProgressRecordMustNotHaveBeenUpdated(); - A.CallTo( - () => progressDataService.CreateNewDelegateProgress( - reusableDelegateDetails.Id, - reusableGroupCourse.CustomisationId, - reusableGroupCourse.CurrentVersion, - null, - 3, - null, - A._, - A._, - testDate - ) - ).MustHaveHappened(); - A.CallTo(() => progressDataService.CreateNewAspProgress(GenericRelatedTutorialId, GenericNewProgressId)) - .MustHaveHappened(); + + A.CallTo(() => progressDataService.CreateNewDelegateProgress( + A._, + A._, + A._, + A._, + A._, + A._, + A._, + A._, + A._ + )).MustNotHaveHappened(); + + A.CallTo(() => progressDataService.CreateNewAspProgress( + A._, + A._ + )).MustNotHaveHappened(); } + } [Test] diff --git a/DigitalLearningSolutions.Web.Tests/TestHelpers/ErrorMessagesTestHelper.cs b/DigitalLearningSolutions.Web.Tests/TestHelpers/ErrorMessagesTestHelper.cs new file mode 100644 index 0000000000..7636944e7a --- /dev/null +++ b/DigitalLearningSolutions.Web.Tests/TestHelpers/ErrorMessagesTestHelper.cs @@ -0,0 +1,17 @@ + +namespace DigitalLearningSolutions.Web.Tests.TestHelpers +{ + public static class ErrorMessagesTestHelper + { + public const string InvalidFormatError = + "Invalid professional registration number format. " + + "Valid formats include: 7 digits (e.g., 1234567), 1–2 letters followed by 6 digits (e.g., AB123456), " + + "4–8 digits, an optional 'P' plus 5–6 digits, 'C' or 'P' plus 6 digits, " + + "an optional letter plus 5–6 digits, 'L' plus 4–6 digits, " + + "or 2 digits followed by a hyphen and 4–5 alphanumeric characters (e.g., 12-AB123)."; + + public const string MissingNumberError = "Enter a professional registration number"; + public const string LengthError = "Professional registration number must be between 5 and 20 characters"; + + } +} diff --git a/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs b/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs index 937fb6d7fb..b5849bebce 100644 --- a/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs +++ b/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs @@ -27,7 +27,8 @@ public static CurrentSelfAssessment CreateDefaultSelfAssessment( bool useDescriptionExpanders = true, string vocabulary = "Capability", string verificationRoleName = "Supervisor", - string signOffRoleName = "Supervisor" + string signOffRoleName = "Supervisor", + bool includeLearnerDeclarationPrompt = true ) { return new CurrentSelfAssessment @@ -47,6 +48,7 @@ public static CurrentSelfAssessment CreateDefaultSelfAssessment( Vocabulary = vocabulary, VerificationRoleName = verificationRoleName, SignOffRoleName = signOffRoleName, + IncludeLearnerDeclarationPrompt = includeLearnerDeclarationPrompt }; } diff --git a/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs b/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs index e55cc5fe1a..5344c99af0 100644 --- a/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs +++ b/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs @@ -9,9 +9,9 @@ public static class SupervisorTagTestHelper private static readonly IClockUtility ClockUtility = new ClockUtility(); public static SupervisorDelegateDetail CreateDefaultSupervisorDelegateDetail( - int id =1, + int id = 1, string supervisorEmail = "email@test.com", - string SupervisorName = "Supervisor", + string SupervisorName = "Supervisor", int? supervisorAdminID = 1, int centreId = 101, string delegateEmail = "email@test.com", @@ -20,10 +20,10 @@ public static SupervisorDelegateDetail CreateDefaultSupervisorDelegateDetail( DateTime? removed = null, string? firstName = null, string? lastName = null, - string candidateNumber = "DELEGATE", + string candidateNumber = "DELEGATE", string candidateEmail = "email@test.com", - string? jobGroupName =null, - string? customPrompt1 =null, + string? jobGroupName = null, + string? customPrompt1 = null, string? answer1 = null, string? customPrompt2 = null, string? answer2 = null, @@ -36,13 +36,13 @@ public static SupervisorDelegateDetail CreateDefaultSupervisorDelegateDetail( string? customPrompt6 = null, string? answer6 = null, string? supervisorName = null, - int candidateAssessmentCount =0, - Guid? InviteHash =null, + int candidateAssessmentCount = 0, + Guid? InviteHash = null, bool delegateIsNominatedSupervisor = false, bool delegateIsSupervisor = false, string professionalRegistrationNumber = "string.Empty", int? delegateID = 0, - bool? active =false + bool? active = false ) { return new SupervisorDelegateDetail @@ -52,23 +52,23 @@ public static SupervisorDelegateDetail CreateDefaultSupervisorDelegateDetail( FirstName = firstName, LastName = lastName, CentreId = centreId, - CandidateAssessmentCount = candidateAssessmentCount, - CandidateNumber = candidateNumber, - CandidateEmail = candidateEmail, - Answer1 = answer1, - Answer2 = answer2, - Answer3 = answer3, - Answer4 = answer4, - Answer5 = answer5, - Answer6 = answer6, - JobGroupName = jobGroupName, - DelegateEmail = delegateEmail, - DelegateID = delegateID, - DelegateIsNominatedSupervisor= delegateIsNominatedSupervisor, - DelegateIsSupervisor= delegateIsSupervisor, - DelegateUserID = delegateUserID, + CandidateAssessmentCount = candidateAssessmentCount, + CandidateNumber = candidateNumber, + CandidateEmail = candidateEmail, + Answer1 = answer1, + Answer2 = answer2, + Answer3 = answer3, + Answer4 = answer4, + Answer5 = answer5, + Answer6 = answer6, + JobGroupName = jobGroupName, + DelegateEmail = delegateEmail, + DelegateID = delegateID, + DelegateIsNominatedSupervisor = delegateIsNominatedSupervisor, + DelegateIsSupervisor = delegateIsSupervisor, + DelegateUserID = delegateUserID, SupervisorAdminID = supervisorAdminID, - SupervisorEmail= supervisorEmail, + SupervisorEmail = supervisorEmail, SupervisorName = supervisorName, CustomPrompt1 = customPrompt1, CustomPrompt2 = customPrompt2, @@ -79,29 +79,29 @@ public static SupervisorDelegateDetail CreateDefaultSupervisorDelegateDetail( Removed = removed, InviteHash = InviteHash, ProfessionalRegistrationNumber = professionalRegistrationNumber, - + }; } public static DelegateSelfAssessment CreateDefaultDelegateSelfAssessment( int id = 1, - int selfAssessmentID =6, - int delegateUserID =1, - string? roleName =null, - bool supervisorSelfAssessmentReview =false, + int selfAssessmentID = 6, + int delegateUserID = 1, + string? roleName = null, + bool supervisorSelfAssessmentReview = false, bool supervisorResultsReview = false, string? supervisorRoleTitle = "Assessor", - DateTime? signedOffDate =null, + DateTime? signedOffDate = null, bool signedOff = false, - DateTime? completeByDate=null, + DateTime? completeByDate = null, int launchCount = 0, - DateTime? completedDate =null, + DateTime? completedDate = null, string? professionalGroup = null, string? questionLabel = null, string? descriptionLabel = null, string? reviewerCommentsLabel = null, string? subGroup = null, - string? roleProfile = null, + string? competencyAssessment = null, int signOffRequested =1, int resultsVerificationRequests =1, bool isSupervisorResultsReviewed =false, @@ -109,17 +109,18 @@ public static DelegateSelfAssessment CreateDefaultDelegateSelfAssessment( bool nonReportable = false ) { - return new DelegateSelfAssessment { + return new DelegateSelfAssessment + { ID = id, SelfAssessmentID = selfAssessmentID, DelegateUserID = delegateUserID, ResultsVerificationRequests = resultsVerificationRequests, - ReviewerCommentsLabel = reviewerCommentsLabel, + ReviewerCommentsLabel = reviewerCommentsLabel, SubGroup = subGroup, - RoleProfile = roleProfile, + CompetencyAssessment = competencyAssessment, SignOffRequested = signOffRequested, SupervisorResultsReview = supervisorResultsReview, - SupervisorSelfAssessmentReview= supervisorSelfAssessmentReview, + SupervisorSelfAssessmentReview = supervisorSelfAssessmentReview, SignedOff = signedOff, CompleteByDate = completeByDate, LaunchCount = launchCount, @@ -130,10 +131,10 @@ public static DelegateSelfAssessment CreateDefaultDelegateSelfAssessment( DescriptionLabel = descriptionLabel, IsAssignedToSupervisor = isAssignedToSupervisor, NonReportable = nonReportable, - IsSupervisorResultsReviewed= isSupervisorResultsReviewed, + IsSupervisorResultsReviewed = isSupervisorResultsReviewed, RoleName = roleName, SupervisorRoleTitle = supervisorRoleTitle, - + }; } diff --git a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs new file mode 100644 index 0000000000..ac127b95a7 --- /dev/null +++ b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs @@ -0,0 +1,1796 @@ +namespace DigitalLearningSolutions.Web.Controllers.CompetencyAssessmentsController +{ + using DigitalLearningSolutions.Data.Enums; + using DigitalLearningSolutions.Data.Extensions; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Data.Models.Frameworks; + using DigitalLearningSolutions.Data.Models.SelfAssessments; + using DigitalLearningSolutions.Web.Attributes; + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.Models.Enums; + using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments; + using DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments; + using DocumentFormat.OpenXml.EMMA; + using GDS.MultiPageFormData.Enums; + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.Rendering; + using Microsoft.Extensions.Logging; + using Serilog.Extensions.Hosting; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + public partial class CompetencyAssessmentsController + { + [Route("/CompetencyAssessments/View/{tabname}/{page=1:int}")] + [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))] + public IActionResult ViewCompetencyAssessments(string tabname, string? searchString = null, + string sortBy = CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentName, + string sortDirection = BaseCompetencyAssessmentsPageViewModel.AscendingText, + int page = 1 + ) + { + var adminId = GetAdminID(); + var isWorkforceManager = GetIsWorkforceManager() | GetIsFrameworkDeveloper(); + var isWorkforceContributor = GetIsWorkforceContributor() | GetIsFrameworkContributor(); + IEnumerable competencyAssessments; + if (tabname == "All") + { + competencyAssessments = competencyAssessmentService.GetAllCompetencyAssessments(adminId); + } + else + { + if (!isWorkforceContributor && !isWorkforceManager) + { + return RedirectToAction("ViewCompetencyAssessments", "CompetencyAssessments", new { tabname = "All" }); + } + competencyAssessments = competencyAssessmentService.GetCompetencyAssessmentsForAdminId(adminId); + } + if (competencyAssessments == null) + { + logger.LogWarning($"Attempt to display competency assessments for admin {adminId} returned null."); + return StatusCode(403); + } + MyCompetencyAssessmentsViewModel myCompetencyAssessments; + AllCompetencyAssessmentsViewModel allCompetencyAssessments; + if (tabname == "Mine") + { + myCompetencyAssessments = new MyCompetencyAssessmentsViewModel( + competencyAssessments, + searchString, + sortBy, + sortDirection, + page, + isWorkforceManager); + allCompetencyAssessments = new AllCompetencyAssessmentsViewModel( + new List(), + searchString, + sortBy, + sortDirection, + page + ); + } + else + { + allCompetencyAssessments = new AllCompetencyAssessmentsViewModel( + competencyAssessments, + searchString, + sortBy, + sortDirection, + page); + myCompetencyAssessments = new MyCompetencyAssessmentsViewModel( + new List(), + searchString, + sortBy, + sortDirection, + page, + isWorkforceManager + ); + } + var currentTab = tabname == "All" ? CompetencyAssessmentsTab.AllCompetencyAssessments : CompetencyAssessmentsTab.MyCompetencyAssessments; + CompetencyAssessmentsViewModel? model = new CompetencyAssessmentsViewModel( + isWorkforceManager, + isWorkforceContributor, + allCompetencyAssessments, + myCompetencyAssessments, + currentTab + ); + return View("Index", model); + } + + [Route("/CompetencyAssessments/{actionName}/Name/{competencyAssessmentId}")] + [Route("/CompetencyAssessments/Framework/{frameworkId}/{actionName}/Name")] + [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/{actionName}/Name")] + [Route("/CompetencyAssessments/{actionName}/Name")] + [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))] + public IActionResult CompetencyAssessmentName(string actionName, int competencyAssessmentId = 0, int? frameworkId = null) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = new CompetencyAssessmentBase(); + if ((frameworkId.HasValue && frameworkId.Value != 0 && actionName == "New")) + { + var data = new CompetencyAssessmentFeaturesViewModel(); + SetcompetencyAssessmentFeaturesData(data); + } + if (competencyAssessmentId > 0) + { + competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Name", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + } + else if (frameworkId != null) + { + var framework = frameworkService.GetBaseFrameworkByFrameworkId((int)frameworkId, adminId); + if (framework != null) + { + competencyAssessmentBase.CompetencyAssessmentName = framework.FrameworkName; + } + } + return View("Name", competencyAssessmentBase); + } + + [HttpPost] + [Route("/CompetencyAssessments/{actionName}/Name/{competencyAssessmentId}")] + [Route("/CompetencyAssessments/Framework/{frameworkId}/{actionName}/Name")] + [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/{actionName}/Name")] + [Route("/CompetencyAssessments/{actionName}/Name")] + [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))] + public IActionResult SaveProfileName(CompetencyAssessmentBase competencyAssessmentBase, string actionName, int competencyAssessmentId = 0, int? frameworkId = null) + { + if (!ModelState.IsValid) + { + ModelState.Remove(nameof(CompetencyAssessmentBase.CompetencyAssessmentName)); + ModelState.AddModelError(nameof(CompetencyAssessmentBase.CompetencyAssessmentName), "Please enter a valid competency assessment name (between 3 and 255 characters)"); + return View("Name", competencyAssessmentBase); + } + else + { + var userCentreId = (int)GetCentreId(); + var adminId = GetAdminID(); + if (actionName == "New") + { + var sameItems = competencyAssessmentService.GetCompetencyAssessmentBaseByName(competencyAssessmentBase.CompetencyAssessmentName.Trim(), GetAdminID()); + if (sameItems != null) + { + ModelState.Remove(nameof(CompetencyAssessmentBase.CompetencyAssessmentName)); + ModelState.AddModelError(nameof(CompetencyAssessmentBase.CompetencyAssessmentName), "Another competency assessment exists with that name. Please choose a different name."); + return View("Name", competencyAssessmentBase); + } + competencyAssessmentId = competencyAssessmentService.InsertCompetencyAssessment(adminId, userCentreId, competencyAssessmentBase.CompetencyAssessmentName.Trim(), frameworkId); + if (frameworkId.HasValue && frameworkId.Value != 0) return RedirectToAction("CompetencyAssessmentFeatures", new { competencyAssessmentId, frameworkId }); + } + else + { + + var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentName(competencyAssessmentBase.ID, adminId, competencyAssessmentBase.CompetencyAssessmentName.Trim()); + if (!isUpdated) + { + ModelState.AddModelError(nameof(CompetencyAssessmentBase.CompetencyAssessmentName), "Another competency assessment exists with that name. Please choose a different name."); + return View("Name", competencyAssessmentBase); + } + if (frameworkId.HasValue && frameworkId.Value != 0 + && competencyAssessmentId != 0 + && actionName == "Edit") return RedirectToAction("CompetencyAssessmentFeatures", new { competencyAssessmentId, frameworkId }); + } + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId, frameworkId }); + } + } + [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Manage")] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Manage")] + public IActionResult ManageCompetencyAssessment(int competencyAssessmentId, int? frameworkId = null) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Competency Assessment", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + + bool hasCompetencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId).Any(); + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, frameworkId); + var model = new ManageCompetencyAssessmentViewModel(competencyAssessmentBase, competencyAssessmentTaskStatus, hasCompetencies); + return View("ManageCompetencyAssessment", model); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/NationalRoleProfileLinks/{actionName}")] + [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))] + public IActionResult EditRoleProfileLinks(int competencyAssessmentId = 0, string actionName = "EditGroup") + { + var adminId = GetAdminID(); + CompetencyAssessmentBase? competencyAssessmentBase; + if (competencyAssessmentId > 0) + { + competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "National Role Profile Links", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + } + else + { + competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + } + var professionalGroups = competencyAssessmentService.GetNRPProfessionalGroups(); + var subGroups = competencyAssessmentService.GetNRPSubGroups(competencyAssessmentBase.NRPProfessionalGroupID); + var roles = competencyAssessmentService.GetNRPRoles(competencyAssessmentBase.NRPSubGroupID); + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new EditRoleProfileLinksViewModel(competencyAssessmentBase, professionalGroups, subGroups, roles, actionName, competencyAssessmentTaskStatus.NationalRoleProfileTaskStatus); + return View(model); + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/NationalRoleProfileLinks/{actionName}")] + public IActionResult EditRoleProfileLinks(EditRoleProfileLinksViewModel model, string actionName, int competencyAssessmentId = 0) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + competencyAssessmentService.UpdateRoleProfileLinksTaskStatus(model.ID, model.TaskStatus ?? false); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit National Role Profile Links", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + if (competencyAssessmentBase.NRPProfessionalGroupID != model.ProfessionalGroupId) + { + model.SubGroupId = null; + model.RoleId = null; + } + if (competencyAssessmentBase.NRPSubGroupID != model.SubGroupId) + { + model.RoleId = null; + } + var isUpdated = competencyAssessmentService.UpdateCompetencyRoleProfileLinks(model.ID, adminId, model.ProfessionalGroupId, model.SubGroupId, model.RoleId); + if (model.ActionName == "EditGroup") + { + if (model.ProfessionalGroupId == null) + { + return RedirectToAction("EditRoleProfileLinks", new { actionName = "Summary", competencyAssessmentId }); + } + else + { + return RedirectToAction("EditRoleProfileLinks", new { actionName = "EditSubGroup", competencyAssessmentId }); + } + } + else if (model.ActionName == "EditSubGroup") + { + if (model.SubGroupId == null) + { + return RedirectToAction("EditRoleProfileLinks", new { actionName = "Summary", competencyAssessmentId }); + } + else + { + return RedirectToAction("EditRoleProfileLinks", new { actionName = "EditRole", competencyAssessmentId }); + } + } + else if (model.ActionName == "EditRole") + { + return RedirectToAction("EditRoleProfileLinks", new { actionName = "Summary", competencyAssessmentId }); + } + else + { + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId }); + } + } + + [Route("/CompetencyAssessments/SubGroup/{actionName}/{competencyAssessmentId}")] + [Route("/CompetencyAssessments/SubGroup/{actionName}")] + [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))] + public IActionResult CompetencyAssessmentSubGroup(string actionName, int competencyAssessmentId = 0) + { + return View("SubGroup"); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/Description/")] + public IActionResult EditDescription(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit Description", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new EditDescriptionViewModel(competencyAssessmentBase, competencyAssessmentTaskStatus.IntroductoryTextTaskStatus); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Description/")] + public IActionResult SaveDescription(EditDescriptionViewModel model) + { + if (string.IsNullOrWhiteSpace(StringHelper.StripHtmlTags(model.Description))) + { + ModelState.AddModelError(nameof(EditDescriptionViewModel.Description), "Please enter introductory text"); + } + if (!ModelState.IsValid) + { + return View("EditDescription", model); + } + var adminId = GetAdminID(); + var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentDescription(model.ID, adminId, SanitizerHelper.SanitizeHtmlData(model.Description)); + competencyAssessmentService.UpdateIntroductoryTextTaskStatus(model.ID, model.TaskStatus ?? false); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID }); + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Branding/")] + public IActionResult EditBranding(int competencyAssessmentId) + { + var centreId = GetCentreId(); + var adminId = GetAdminID(); + var brandsList = commonService.GetBrandListForCentre((int)centreId).Select(b => new { b.BrandID, b.BrandName }).ToList(); + var categoryList = commonService.GetCategoryListForCentre((int)centreId).Select(c => new { c.CourseCategoryID, c.CategoryName }).ToList(); + var brandSelectList = new SelectList(brandsList, "BrandID", "BrandName"); + var categorySelectList = new SelectList(categoryList, "CourseCategoryID", "CategoryName"); + var competencyAssessment = competencyAssessmentService.GetCompetencyAssessmentById(competencyAssessmentId, adminId); + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new EditBrandingViewModel(competencyAssessment, brandSelectList, categorySelectList, competencyAssessmentTaskStatus.BrandingTaskStatus); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Branding/")] + public IActionResult EditBranding(EditBrandingViewModel model) + { + var adminId = GetAdminID(); + var centreId = GetCentreId(); + if (!ModelState.IsValid) + { + var brandsList = commonService.GetBrandListForCentre((int)centreId).Select(b => new { b.BrandID, b.BrandName }).ToList(); + var categoryList = commonService.GetCategoryListForCentre((int)centreId).Select(c => new { c.CourseCategoryID, c.CategoryName }).ToList(); + var brandSelectList = new SelectList(brandsList, "BrandID", "BrandName"); + var categorySelectList = new SelectList(categoryList, "CourseCategoryID", "CategoryName"); + model.BrandSelectList = brandSelectList; + model.CategorySelectList = categorySelectList; + return View("EditBranding", model); + } + if (model.BrandID == 0) + { + model.BrandID = commonService.InsertBrandAndReturnId(model.Brand, (int)centreId); + } + if (model.CategoryID == 0) + { + model.CategoryID = commonService.InsertCategoryAndReturnId(model.Category, (int)centreId); + } + var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentBranding(model.ID, adminId, model.BrandID, model.CategoryID); + competencyAssessmentService.UpdateBrandingTaskStatus(model.ID, model.TaskStatus ?? false); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID }); + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Vocabulary/")] + public IActionResult EditVocabulary(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit Vocabulary", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new EditVocabularyViewModel(competencyAssessmentBase, competencyAssessmentTaskStatus.VocabularyTaskStatus); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Vocabulary/")] + public IActionResult SaveVocabulary(EditVocabularyViewModel model) + { + if (!ModelState.IsValid) + { + return View("EditVocabulary", model); + } + var adminId = GetAdminID(); + var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentVocabulary(model.ID, adminId, model.Vocabulary); + competencyAssessmentService.UpdateVocabularyTaskStatus(model.ID, model.TaskStatus ?? false); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID }); + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{actionName}")] + public IActionResult SelectFrameworkSources(int competencyAssessmentId, string actionName) + { + var adminId = GetAdminID(); + var frameworks = frameworkService.GetAllFrameworks(adminId); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + if (competencyAssessmentBase == null) + { + logger.LogWarning($"Failed to load Vocabulary page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}"); + return StatusCode(500); + } + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Select Framework Sources", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var primaryFrameworkId = competencyAssessmentService.GetPrimaryLinkedFrameworkId(competencyAssessmentId); + var additionalFrameworks = competencyAssessmentService.GetLinkedFrameworkIds(competencyAssessmentId); + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new SelectFrameworkSourcesViewModel(competencyAssessmentBase, frameworks, additionalFrameworks, primaryFrameworkId, competencyAssessmentTaskStatus.FrameworkLinksTaskStatus, actionName); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{actionName}")] + public IActionResult SelectFrameworkSources(SelectFrameworkSourcesFormData model, string actionName) + { + var adminId = GetAdminID(); + var competencyAssessmentId = model.CompetencyAssessmentId; + if (!ModelState.IsValid) + { + var frameworks = frameworkService.GetAllFrameworks(adminId); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + + if (competencyAssessmentBase == null) + { + logger.LogWarning($"Failed to load Vocabulary page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}"); + return StatusCode(500); + } + + if (competencyAssessmentBase.UserRole < 2) + { + return StatusCode(403); + } + + var primaryFrameworkId = competencyAssessmentService.GetPrimaryLinkedFrameworkId(competencyAssessmentId); + var additionalFrameworks = competencyAssessmentService.GetLinkedFrameworkIds(competencyAssessmentId); + + var viewModel = new SelectFrameworkSourcesViewModel( + competencyAssessmentBase, + frameworks, + additionalFrameworks, + primaryFrameworkId, + model.TaskStatus, + model.ActionName + ); + + return View("SelectFrameworkSources", viewModel); + } + + if (actionName == "AddFramework") + { + competencyAssessmentService.InsertSelfAssessmentFramework(adminId, competencyAssessmentId, model.FrameworkId.Value); + return RedirectToAction("SelectFrameworkSources", new { competencyAssessmentId, actionName = "Summary" }); + } + else + { + competencyAssessmentService.UpdateFrameworkLinksTaskStatus(model.CompetencyAssessmentId, model.TaskStatus ?? false, null); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.CompetencyAssessmentId }); + } + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Remove")] + public IActionResult RemoveFramework(int frameworkId, int competencyAssessmentId) + { + var frameworkCompetencyCount = competencyAssessmentService.GetCompetencyCountByFrameworkId(competencyAssessmentId, frameworkId); + if (frameworkCompetencyCount > 0) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var framework = frameworkService.GetFrameworkDetailByFrameworkId(frameworkId, adminId); + var model = new ConfirmRemoveFrameworkSourceViewModel(competencyAssessmentBase, framework, frameworkCompetencyCount); + return View("ConfirmRemoveFrameworkSource", model); + } + else + { + var adminId = GetAdminID(); + competencyAssessmentService.RemoveSelfAssessmentFramework(competencyAssessmentId, frameworkId, adminId); + } + return RedirectToAction("SelectFrameworkSources", new { competencyAssessmentId, actionName = "Summary" }); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Remove")] + public IActionResult RemoveFramework(ConfirmRemoveFrameworkSourceViewModel model) + { + if (!ModelState.IsValid) + { + return View("ConfirmRemoveFrameworkSource", model); + } + var adminId = GetAdminID(); + competencyAssessmentService.RemoveFrameworkCompetenciesFromAssessment(model.CompetencyAssessmentId, model.FrameworkId); + competencyAssessmentService.RemoveSelfAssessmentFramework(model.CompetencyAssessmentId, model.FrameworkId, adminId); + return RedirectToAction("SelectFrameworkSources", new { model.CompetencyAssessmentId, actionName = "Summary" }); + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies")] + public IActionResult ViewSelectedCompetencies(int competencyAssessmentId) + { + + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId); + var linkedFrameworks = competencyAssessmentService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId); + if (!competencies.Any()) + { + return RedirectToAction("AddCompetenciesSelectFramework", new { competencyAssessmentId }); + } + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "View Selected Competencies", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new ViewSelectedCompetenciesViewModel(competencyAssessmentBase, competencies, linkedFrameworks, competencyAssessmentTaskStatus.SelectCompetenciesTaskStatus); + return View(model); + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/SelectFramework")] + public IActionResult AddCompetenciesSelectFramework(int competencyAssessmentId) + { + var linkedFrameworks = competencyAssessmentService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId); + if (!linkedFrameworks.Any()) + { + return RedirectToAction("SelectFrameworkSources", new { competencyAssessmentId, actionName = "AddFramework" }); + } + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Select Framework Sources", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var model = new AddCompetenciesSelectFrameworkViewModel(competencyAssessmentBase, linkedFrameworks); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/SelectFramework")] + public IActionResult AddCompetenciesSelectFramework(AddCompetenciesSelectFrameworkFormData formdata) + { + if (!ModelState.IsValid) + { + var competencyAssessmentId = formdata.ID; + var linkedFrameworks = competencyAssessmentService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId); + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var model = new AddCompetenciesSelectFrameworkViewModel(competencyAssessmentBase, linkedFrameworks); + model.FrameworkId = formdata.FrameworkId; + return View("AddCompetenciesSelectFramework", model); + } + else + { + return RedirectToAction("AddCompetencies", new { competencyAssessmentId = formdata.ID, frameworkId = formdata.FrameworkId }); + } + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/{frameworkId}")] + public IActionResult AddCompetencies(int competencyAssessmentId, int frameworkId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Add Competencies", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var framework = frameworkService.GetBaseFrameworkByFrameworkId(frameworkId, adminId); + var selectedFrameworkCompetencies = competencyAssessmentService.GetLinkedFrameworkCompetencyIds(competencyAssessmentId, frameworkId); + var groupedCompetencies = frameworkService.GetFrameworkCompetencyGroups(frameworkId, competencyAssessmentId); + var ungroupedCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId, competencyAssessmentId); + var competencyIds = ungroupedCompetencies.Select(c => c.CompetencyID).ToArray(); + var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds); + foreach (var competency in ungroupedCompetencies) + competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID); + foreach (var group in groupedCompetencies) + { + competencyIds = group.FrameworkCompetencies.Select(c => c.CompetencyID).ToArray(); + competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds); + foreach (var competency in group.FrameworkCompetencies) + competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID); + } + var model = new AddCompetenciesViewModel(competencyAssessmentBase, groupedCompetencies, ungroupedCompetencies, frameworkId, framework.FrameworkName, selectedFrameworkCompetencies); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/{frameworkId}")] + public IActionResult AddComptencies(AddCompetenciesFormData model, int competencyAssessmentId, int frameworkId) + { + if (!ModelState.IsValid) + { + //reload model and view + } + if (model.SelectedCompetencyIds != null) + { + competencyAssessmentService.InsertCompetenciesIntoAssessmentFromFramework(model.SelectedCompetencyIds, frameworkId, competencyAssessmentId); + } + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + if (competencyAssessmentTaskStatus.SelectCompetenciesTaskStatus != true) + { + competencyAssessmentService.UpdateSelectCompetenciesTaskStatus(competencyAssessmentId, false, null); + } + return RedirectToAction("ViewSelectedCompetencies", new { competencyAssessmentId }); + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Delete/{competencyId}")] + public IActionResult DeleteCompetency(int competencyAssessmentId, int competencyId) + { + competencyAssessmentService.RemoveCompetencyFromAssessment(competencyAssessmentId, competencyId); + return RedirectToAction("ViewSelectedCompetencies", new { competencyAssessmentId }); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/CompetencyGroup/Delete/{competencyGroupId}/{competencyCount}/Confirm")] + public IActionResult DeleteCompetencyGroupConfirm(int competencyAssessmentId, int competencyGroupId, int competencyCount) + { + var adminId = GetAdminID(); + CompetencyAssessmentBase? competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + if (competencyAssessmentBase == null) + { + logger.LogWarning($"Failed to load DeleteCompetencyGroupConfirm page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}"); + return StatusCode(500); + } + if (competencyAssessmentBase.UserRole < 2) + { + return StatusCode(403); + } + var model = new CompetencyGroupDeleteViewModel(competencyAssessmentId, competencyGroupId, competencyCount, competencyAssessmentBase.Vocabulary); + + return View("RemoveCompetencyGroupConfirm", model); + } + + public IActionResult DeleteCompetencyGroup(int competencyAssessmentId, int competencyGroupId) + { + competencyAssessmentService.RemoveCompetencyGroupFromAssessment(competencyAssessmentId, competencyGroupId); + return RedirectToAction("ViewSelectedCompetencies", new { competencyAssessmentId }); + } + + + public IActionResult MoveCompetencyInSelfAssessment(int competencyAssessmentId, int competencyId, string direction) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Move Competency In Self Assessment", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + competencyAssessmentService.MoveCompetencyInSelfAssessment(competencyAssessmentId, competencyId, direction); + return new RedirectResult(Url.Action("ViewSelectedCompetencies", new { competencyAssessmentId }) + "#competency-" + competencyId.ToString()); + } + public IActionResult MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, int groupId, string direction) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Move Competency Group In Self Assessment", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + competencyAssessmentService.MoveCompetencyGroupInSelfAssessment(competencyAssessmentId, groupId, direction); + return new RedirectResult(Url.Action("ViewSelectedCompetencies", new { competencyAssessmentId }) + "#group-" + groupId.ToString()); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies")] + public IActionResult ViewSelectedCompetencies(ViewSelectedCompetenciesFormData model) + { + if (model.TaskStatus == null) + { + model.TaskStatus = false; + } + competencyAssessmentService.UpdateSelectCompetenciesTaskStatus(model.ID, model.TaskStatus.Value, null); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Manage")] + public IActionResult ManageOptionalCompetencies(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Optional Competencies", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId).Where(c => c.Optional == true); + var competencyIds = competencies.Select(c => c.CompetencyID).ToArray(); + var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds); + foreach (var competency in competencies) + competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID); + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new SelectOptionalCompetenciesViewModel(competencyAssessmentBase, competencies, competencyAssessmentTaskStatus.OptionalCompetenciesTaskStatus); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Manage")] + public IActionResult ManageOptionalCompetencies(ViewSelectedCompetenciesFormData model) + { + if (model.TaskStatus == null) + { + model.TaskStatus = false; + } + competencyAssessmentService.UpdateOptionalCompetenciesTaskStatus(model.ID, model.TaskStatus.Value, null); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Select")] + public IActionResult SelectOptionalCompetencies(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Select Optional Competencies", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId); + var competencyIds = competencies.Select(c => c.CompetencyID).ToArray(); + var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds); + foreach (var competency in competencies) + competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID); + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new SelectOptionalCompetenciesViewModel(competencyAssessmentBase, competencies, competencyAssessmentTaskStatus.OptionalCompetenciesTaskStatus); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Select")] + public IActionResult SelectOptionalCompetencies(SelectOptionalCompetenciesFormData model) + { + if (!ModelState.IsValid) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.ID, adminId); + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(model.ID); + var competencyIds = competencies.Select(c => c.CompetencyID).ToArray(); + var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds); + foreach (var competency in competencies) + competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID); + var viewModel = new SelectOptionalCompetenciesViewModel(competencyAssessmentBase, competencies, model.TaskStatus); + return View("SelectOptionalCompetencies", viewModel); + } + competencyAssessmentService.UpdateOptionalCompetenciesInAssessment(model.ID, model.GroupIds ?? [], model.SelectedCompetencyIds ?? []); + return RedirectToAction("ManageOptionalCompetencies", new { competencyAssessmentId = model.ID }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/SetMinimum")] + public IActionResult SetMinimumOptionalCompetencies(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Minimum Optional Competencies", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId); + var viewModel = new SetMinimumOptionalCompetenciesViewModel(competencyAssessmentBase, competencies); + return View("SetMinimumOptionalCompetencies", viewModel); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/SetMinimum")] + public IActionResult SetMinimumOptionalCompetencies(SetMinimumOptionalCompetenciesFormData model) + { + if (!ModelState.IsValid) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.ID, adminId); + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(model.ID); + var viewModel = new SetMinimumOptionalCompetenciesViewModel(competencyAssessmentBase, competencies); + return View("SetMinimumOptionalCompetencies", viewModel); + } + competencyAssessmentService.UpdateMinimumOptionalCompetencies(model.ID, model.MinimumOptionalCompetencies ?? 0); + return RedirectToAction("ManageOptionalCompetencies", new { competencyAssessmentId = model.ID }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/LearnerPrompt")] + public IActionResult SetOptionalCompetencyLearnerPrompt(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Optional Competency Learner Prompt", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId); + var viewModel = new SetOptionalCompetencyLearnerPromptViewModel(competencyAssessmentBase, competencies); + return View("SetOptionalCompetencyLearnerPrompt", viewModel); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/LearnerPrompt")] + public IActionResult SetOptionalCompetencyLearnerPrompt(SetOptionalCompetencyLearnerPromptFormData model) + { + if (!ModelState.IsValid) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.ID, adminId); + var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(model.ID); + var viewModel = new SetOptionalCompetencyLearnerPromptViewModel(competencyAssessmentBase, competencies); + return View("SetOptionalCompetencyLearnerPrompt", viewModel); + } + competencyAssessmentService.UpdateManageOptionalCompetenciesPrompt(model.ID, model.ManageOptionalCompetenciesPrompt); + return RedirectToAction("ManageOptionalCompetencies", new { competencyAssessmentId = model.ID }); + } + + [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Features")] + public IActionResult CompetencyAssessmentFeatures(int competencyAssessmentId, int? frameworkId = null) + { + var adminId = GetAdminID(); + var data = GetcompetencyAssessmentFeaturesData(); + if (!string.IsNullOrEmpty(data.CompetencyAssessmentName)) return View(data); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Features", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var baseModel = new CompetencyAssessmentFeaturesViewModel(competencyAssessmentBase.ID, + competencyAssessmentBase.CompetencyAssessmentName, + competencyAssessmentBase.UserRole, + frameworkId); + return View(baseModel); + } + [HttpPost] + [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Features")] + public IActionResult CompetencyAssessmentFeatures(CompetencyAssessmentFeaturesViewModel featuresViewModel) + { + if (featuresViewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 }); + SetcompetencyAssessmentFeaturesData(featuresViewModel); + return RedirectToAction("CompetencyAssessmentSummary", new { competencyAssessmentId = featuresViewModel.ID, featuresViewModel.FrameworkId }); + } + + [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Summary")] + public IActionResult CompetencyAssessmentSummary(int competencyAssessmentId, int? frameworkId = null) + { + if (competencyAssessmentService.GetSelfAssessmentStructure(competencyAssessmentId) != 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 }); + if (competencyAssessmentId == 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + var data = GetcompetencyAssessmentFeaturesData(); + if (data == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 }); + SetcompetencyAssessmentFeaturesData(data); + return View(data); + } + [HttpPost] + [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Summary")] + public IActionResult CompetencyAssessmentSummary(CompetencyAssessmentFeaturesViewModel competency) + { + var data = GetcompetencyAssessmentFeaturesData(); + if (competencyAssessmentService.GetSelfAssessmentStructure(data.ID) != 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 }); + if (data.ID == 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + var features = competencyAssessmentService.UpdateCompetencyAssessmentFeaturesTaskStatus(data.ID, + data.DescriptionStatus, + data.ProviderandCategoryStatus, + data.VocabularyStatus, + data.WorkingGroupStatus, + data.AllframeworkCompetenciesStatus); + if (!features) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 }); + competencyAssessmentService.UpdateSelfAssessmentFromFramework(data.ID, data.FrameworkId); + competencyAssessmentService.InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(data.ID, data.FrameworkId); + var insertSelfAssessmentGroupedCompetencies = competencyAssessmentService.InsertSelfAssessmentGroupedCompetencies(data.ID, data.FrameworkId); + var insertSelfAssessmentUngroupedCompetencies = competencyAssessmentService.InsertSelfAssessmentUngroupedCompetencies(data.ID, data.FrameworkId); + if (!insertSelfAssessmentGroupedCompetencies) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 }); + multiPageFormService.ClearMultiPageFormData(MultiPageFormDataFeature.AddCustomWebForm("AssessmentFeaturesDataCWF"), TempData); + TempData.Clear(); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = competency.ID, competency.FrameworkId }); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Make")] + public IActionResult ConfirmMaKePrimaryFramework(int frameworkId, int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Confirm MaKe Primary Framework", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var framework = frameworkService.GetFrameworkDetailByFrameworkId(frameworkId, adminId); + var model = new ConfirmMakePrimaryFrameworkViewModel(competencyAssessmentBase, framework); + return View("ConfirmMaKePrimaryFramework", model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Make")] + public IActionResult ConfirmMaKePrimaryFramework(ConfirmMakePrimaryFrameworkViewModel model) + { + if (!ModelState.IsValid) + { + return View("ConfirmMaKePrimaryFramework", model); + } + competencyAssessmentService.UpdatePrimaryFrameworkCompetencies(model.CompetencyAssessmentId, model.FrameworkId); + var features = competencyAssessmentService.UpdateCompetencyAssessmentFeaturesTaskStatus(model.CompetencyAssessmentId, + model.DescriptionStatus, + model.ProviderandCategoryStatus, + model.VocabularyStatus, + model.WorkingGroupStatus, + model.AllframeworkCompetenciesStatus); + + if (model.WorkingGroupStatus) + competencyAssessmentService.InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(model.CompetencyAssessmentId, model.FrameworkId); + + competencyAssessmentService.UpdateSelfAssessmentFromFramework(model.CompetencyAssessmentId, model.FrameworkId); + + if (model.AllframeworkCompetenciesStatus) + { + var insertSelfAssessmentGroupedCompetencies = competencyAssessmentService.InsertSelfAssessmentGroupedCompetencies(model.CompetencyAssessmentId, model.FrameworkId); + var insertSelfAssessmentUngroupedCompetencies = competencyAssessmentService.InsertSelfAssessmentUngroupedCompetencies(model.CompetencyAssessmentId, model.FrameworkId); + } + return RedirectToAction("ManageCompetencyAssessment", new { model.CompetencyAssessmentId, model.FrameworkId }); + } + + + [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}")] + public IActionResult AssessmentWorkingGroup(int competencyAssessmentId, string actionName) + { + var adminId = GetAdminID(); + + var collaborators = competencyAssessmentService.GetCollaboratorsForCompetencyAssessmentId(competencyAssessmentId); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Assessment Working Group", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new WorkingGroupCollaboratorsViewModel() + { + CompetencyAssessmentID = competencyAssessmentId, + Collaborators = collaborators, + CompetencyAssessmentTaskStatus = taskStatus.WorkingGroupTaskStatus, + UserEmail = null, + Error = false, + ActionName = actionName + }; + if (TempData["CompetencyAssessmentError"] != null) + { + ModelState.AddModelError("userEmail", TempData.Peek("CompetencyAssessmentError").ToString()); + } + return View("CompetencyAssessmentWorkingGroup", model); + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}")] + public IActionResult AssessmentWorkingGroup(WorkingGroupCollaboratorsViewModel model, bool canModify, string actionName) + { + int? centreID = GetCentreId(); + if (actionName == "Collaborators" || actionName == "CollaboratorReview") + { + var collaboratorId = competencyAssessmentService.AddCollaboratorToCompetencyAssessment(model.CompetencyAssessmentID, model.UserEmail, canModify, centreID); + if (collaboratorId > 0) + { + selfAssessmentNotificationService.SendCompetencyAssessmentCollaboratorInvite(collaboratorId, GetAdminID()); + } + else + { + if (collaboratorId == -3) + { + TempData["CompetencyAssessmentError"] = "Email address should not be empty"; + + } + else if (collaboratorId == -2) + { + TempData["CompetencyAssessmentError"] = $"A user with the email address has been previously added"; + } + else if (collaboratorId == -4) + { + TempData["CompetencyAssessmentError"] = $"The email address must match a registered DLS Admin account"; + } + else if (collaboratorId == -5) + { + TempData["CompetencyAssessmentError"] = $"The owner cannot be the collaborator of the competency assessment."; + } + else + { + TempData["CompetencyAssessmentError"] = "User not added,Kindly try again;"; + } + } + return RedirectToAction("AssessmentWorkingGroup", "CompetencyAssessments", new { model.CompetencyAssessmentID, actionName = actionName }); + + } + else + { + competencyAssessmentService.UpdateWorkingGroupTaskStatus(model.CompetencyAssessmentID, model.CompetencyAssessmentTaskStatus ?? false, null); + return RedirectToAction("ManageCompetencyAssessment", "CompetencyAssessments", new { model.CompetencyAssessmentID }); + } + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/ConfigureOptions")] + public IActionResult ConfigureOptions(int competencyAssessmentId) + { + var data = new OptionsLabelsViewModel(); + var adminId = GetAdminID(); + + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "competency assessment options", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + data.CompetencyAssessmentID = competencyAssessmentId; + data.Vocabulary = competencyAssessmentBase.Vocabulary; + data.IncludeLearnerDeclarationPrompt = competencyAssessmentBase.IncludeLearnerDeclarationPrompt; + data.IncludesSignposting = competencyAssessmentBase.IncludesSignposting; + data.LinearNavigation = competencyAssessmentBase.LinearNavigation; + data.UseDescriptionExpanders = competencyAssessmentBase.UseDescriptionExpanders; + data.QuestionLabel = string.IsNullOrWhiteSpace(competencyAssessmentBase.QuestionLabel) ? false : true; + data.QuestionLabelText = competencyAssessmentBase.QuestionLabel?.Trim(); + data.ReviewerCommentsLabel = string.IsNullOrWhiteSpace(competencyAssessmentBase.ReviewerCommentsLabel) ? false : true; + data.ReviewerCommentsLabelText = competencyAssessmentBase.ReviewerCommentsLabel?.Trim(); + data.IsSupervisionSwitchedOn = competencyAssessmentBase.SupervisorSelfAssessmentReview && competencyAssessmentBase.SupervisorResultsReview; + data.IsSignpostedLearning = competencyAssessmentService.HasCompetencyWithSignpostedLearning(competencyAssessmentId); + + var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + data.SelfAssessmentOptionsTaskStatus = taskStatus.SelfAssessmentOptionsTaskStatus; + + SetOptionsLabelsData(data); + + var step = (int)OptionLabel.Declaration; + if (taskStatus.SelfAssessmentOptionsTaskStatus != null) + step = (int)OptionLabel.Summary; + + ValidateStep(data, ref step); + return RedirectToAction("OptionsLabels", "CompetencyAssessments", new { competencyAssessmentId, step }); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/OptionsLabels/{step}")] + public IActionResult OptionsLabels(int competencyAssessmentId, int step) + { + if (step < (int)OptionLabel.Declaration || step > (int)OptionLabel.Summary) + return StatusCode(500); + + var adminId = GetAdminID(); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "competency assessment options"); + if (result.StatusCode != 200) + return result; + + var data = GetOptionsLabelslData(); + + if (ValidateStep(data, ref step)) + { + return RedirectToAction("OptionsLabels", "CompetencyAssessments", new { competencyAssessmentId, step }); + } + + data.CurrentStep = step; + var model = new OptionsLabelsViewModel(data); + + return View("CompetencyAssessmentOptions", model); + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/OptionsLabels/{step}")] + public IActionResult OptionsLabels(OptionsLabelsViewModel model) + { + var adminId = GetAdminID(); + var data = GetOptionsLabelslData(); + data.CurrentStep = model.CurrentStep; + ModelState.Remove("VocabularySingular"); + ModelState.Remove("VocabularyPlural"); + + if (model.CurrentStep == (int)OptionLabel.Declaration) + { + data.IncludeLearnerDeclarationPrompt = model.IncludeLearnerDeclarationPrompt; + } + else if (model.CurrentStep == (int)OptionLabel.Signposting) + { + data.IncludesSignposting = model.IncludesSignposting; + } + else if (model.CurrentStep == (int)OptionLabel.LinearNavigation) + { + data.LinearNavigation = model.LinearNavigation; + } + else if (model.CurrentStep == (int)OptionLabel.DescriptionExpanders) + { + data.UseDescriptionExpanders = model.UseDescriptionExpanders; + } + else if (model.CurrentStep == (int)OptionLabel.QuestionLabels) + { + if (model.QuestionLabel) + { + var label = model.QuestionLabelText?.Trim(); + if (string.IsNullOrEmpty(label)) + ModelState.AddModelError(nameof(model.QuestionLabelText), "Please enter a question label"); + else if (label.Length > 50) + ModelState.AddModelError(nameof(model.QuestionLabelText), "Question label must be 50 characters or fewer"); + + if (!ModelState.IsValid) + { + var errModel = new OptionsLabelsViewModel(data); + errModel.QuestionLabel = model.QuestionLabel; + errModel.QuestionLabelText = model.QuestionLabelText; + errModel.Error = true; + return View("CompetencyAssessmentOptions", errModel); + } + } + data.QuestionLabel = model.QuestionLabel; + data.QuestionLabelText = model.QuestionLabel ? model.QuestionLabelText.Trim() : null; + } + else if (model.CurrentStep == (int)OptionLabel.CommentsLabel) + { + if (model.ReviewerCommentsLabel) + { + var label = model.ReviewerCommentsLabelText?.Trim(); + if (string.IsNullOrEmpty(label)) + ModelState.AddModelError(nameof(model.ReviewerCommentsLabelText), "Please enter a reviewer comment"); + else if (label.Length > 50) + ModelState.AddModelError(nameof(model.ReviewerCommentsLabelText), "Reviewer comment must be 50 characters or fewer"); + + if (!ModelState.IsValid) + { + var errModel = new OptionsLabelsViewModel(data); + errModel.ReviewerCommentsLabel = model.ReviewerCommentsLabel; + errModel.ReviewerCommentsLabelText = model.ReviewerCommentsLabelText; + errModel.Error = true; + return View("CompetencyAssessmentOptions", errModel); + } + } + data.ReviewerCommentsLabel = model.ReviewerCommentsLabel; + data.ReviewerCommentsLabelText = model.ReviewerCommentsLabel ? model.ReviewerCommentsLabelText.Trim() : null; + } + else if (model.CurrentStep == (int)OptionLabel.Summary) + { + var isUpdate = competencyAssessmentService.UpdateCompetencyAssessmentOptions( + data.IncludeLearnerDeclarationPrompt, + data.IncludesSignposting, + data.LinearNavigation, + data.UseDescriptionExpanders, + data.QuestionLabel ? data.QuestionLabelText?.Trim() : null, + data.ReviewerCommentsLabel ? data.ReviewerCommentsLabelText?.Trim() : null, + data.CompetencyAssessmentID, + adminId); + if (!isUpdate) + { + ModelState.AddModelError("", "Update failed. Please try again."); + return View("CompetencyAssessmentOptions", model); + } + competencyAssessmentService.UpdateCompetencyAssessmentOptionsTaskStatus(model.CompetencyAssessmentID, model.SelfAssessmentOptionsTaskStatus ?? false); + return RedirectToAction("ManageCompetencyAssessment", "CompetencyAssessments", new { model.CompetencyAssessmentID }); + } + + if (data.SelfAssessmentOptionsTaskStatus != null) + data.CurrentStep = (int)OptionLabel.Summary; + else + data.CurrentStep = model.CurrentStep + 1; + + SetOptionsLabelsData(data); + + var newModel = new OptionsLabelsViewModel(data); + return RedirectToAction("OptionsLabels", "CompetencyAssessments", new { model.CompetencyAssessmentID, step = newModel.CurrentStep }); + } + + public IActionResult RemoveCollaborator(int competencyAssessmentId, int id, string actionName) + { + competencyAssessmentService.RemoveCollaboratorFromCompetencyAssessment(competencyAssessmentId, id); + return RedirectToAction("AssessmentWorkingGroup", "CompetencyAssessments", new { competencyAssessmentId, actionName = actionName }); + } + + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/")] + public IActionResult ManageCompetencyRoleRequirements(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Competency Role Requirements", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var groupedCompetencyWithAssessmentRoleRequirements = competencyAssessmentService.GetGroupedCompetencyWithAssessmentRoleRequirements(competencyAssessmentId, null, null); + var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + var model = new ManageCompetencyRoleRequirementsViewModel(competencyAssessmentBase, groupedCompetencyWithAssessmentRoleRequirements, taskStatus); + return View("ManageCompetencyRoleRequirements", model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/")] + public IActionResult ManageCompetencyRoleRequirements(ManageCompetencyRoleRequirementsFormData model) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.Id, adminId); + var result = ValidateCompetencyAssessmentAndRole(model.Id, adminId, "Manage Competency Role Requirements", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + competencyAssessmentService.UpdateCompetencyAssessmentRoleRequirementsTaskStatus(model.Id, model.TaskStatus ?? false, null); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.Id }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Enforce")] + public IActionResult EditEnforceRoleRequirementsForSignOff(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Enforce Role Requirements For Sign Off", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var model = new EditRoleRequirementsFlagsViewModel(competencyAssessmentBase); + return View("EnforceRoleRequirementsForSignOff", model); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/IncludeFilters")] + public IActionResult EditIncludeRequirementsFilters(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Enforce Role Requirements For Sign Off", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var model = new EditRoleRequirementsFlagsViewModel(competencyAssessmentBase); + return View("EditIncludeRequirementsFilters", model); + } + [HttpPost] + public IActionResult EditRoleRequirementsFlags(ManageCompetencyRoleRequirementsFormData model) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.Id, adminId); + var result = ValidateCompetencyAssessmentAndRole(model.Id, adminId, "Set Enforce Role Requirements For Sign Off", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + competencyAssessmentService.UpdateRoleRequirementsFlags(model.Id, model.EnforceRoleRequirementsForSignOff, model.IncludeRequirementsFilters); + return RedirectToAction("ManageCompetencyRoleRequirements", new { competencyAssessmentId = model.Id }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Edit")] + public IActionResult EditCompetencyRoleRequirements(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Competency Role Requirements", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var groupedCompetencyWithAssessmentRoleRequirements = competencyAssessmentService.GetGroupedCompetencyWithAssessmentRoleRequirements(competencyAssessmentId, null, null); + var model = new EditCompetencyRoleRequirementsViewModel(competencyAssessmentBase, groupedCompetencyWithAssessmentRoleRequirements); + return View("EditCompetencyRoleRequirements", model); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Edit/Competency/{competencyId}/Question/{assessmentQuestionId}")] + public IActionResult EditQuestionResponseRoleRequirements(int competencyAssessmentId, int competencyId, int assessmentQuestionId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit Competency Role Requirement Question Cells", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var assessmentQuestion = competencyAssessmentService.GetGroupedCompetencyWithAssessmentRoleRequirements(competencyAssessmentId, competencyId, assessmentQuestionId); + var countAssessmentQuestionInSelfAssessment = competencyAssessmentService.GetCountOfAsssessmentQuestionInCompetencyAssessment(competencyAssessmentId, assessmentQuestionId); + var model = new EditQuestionResponseRoleRequirementsViewModel(competencyAssessmentBase, assessmentQuestion, countAssessmentQuestionInSelfAssessment, competencyId, assessmentQuestionId); + return View("EditQuestionResponseRoleRequirements", model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Edit/Competency/{competencyId}/Question/{assessmentQuestionId}")] + public IActionResult EditQuestionResponseRoleRequirements(EditQuestionResponseRoleRequirementsFormData model) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.Id, adminId); + var result = ValidateCompetencyAssessmentAndRole(model.Id, adminId, "Edit Competency Role Requirement Question Cells", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + if (model.ApplyToAll) + { + competencyAssessmentService.UpdateAssessmentQuestionRoleRequirementsForSelfAssessment(model.Id, model.AssessmentQuestionId, model.ResponseRoleRequirements); + } + else + { + competencyAssessmentService.UpdateCompetencyAssessmentQuestionRoleRequirement(model.Id, model.CompetencyId, model.AssessmentQuestionId, model.ResponseRoleRequirements); + } + + return RedirectToAction("EditCompetencyRoleRequirements", new { competencyAssessmentId = model.Id }); + } + [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisorRoles")] + public IActionResult SupervisorRoles(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervisor Roles", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + + var model = new ManagesupervisionViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName, + competencyAssessmentBase.SupervisorResultsReview, + competencyAssessmentBase.SupervisorSelfAssessmentReview, + competencyAssessmentBase.SignOffSupervisorStatement, + competencyAssessmentBase.SignOffRequestorStatement, + this.config.GetLearnerDefaultText(), + this.config.GetSupervisorDefaultText()); + + SetManagesupervisionData(model); + + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + if (competencyAssessmentTaskStatus.SupervisorRolesTaskStatus != null) + return RedirectToAction("ManageSupervisionSettings", "CompetencyAssessments", + new + { + CompetencyAssessmentId = competencyAssessmentId, + ActionName = "SupervisorRoles" + }); + return RedirectToAction("SupervisedSelfAssessmentSignoff", "CompetencyAssessments", new { competencyAssessmentId }); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/Supervised")] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/Supervised")] + public IActionResult SupervisedSelfAssessmentSignoff(int competencyAssessmentId, string? actionName) + { + if (actionName == "Signoff") + { + var data = GetManagesupervisionData(); + data.Signoff.ActionName = actionName; + var models = new SupervisedSelfAssessmentSignoffViewModel(data.Signoff); + return View(models); + } + var adminId = GetAdminID(); + var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervised Self Assessment Signoff", baseData); + if (result.StatusCode != 200) + return result; + var model = new SupervisedSelfAssessmentSignoffViewModel(competencyAssessmentId, baseData.CompetencyAssessmentName, actionName); + return View("SupervisedSelfAssessmentSignoff", model); + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Supervised")] + public IActionResult SupervisedSelfAssessmentSignoff(SupervisedSelfAssessmentSignoffViewModel supervisedSelf) + { + if (supervisedSelf == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + if (supervisedSelf.Signoff == null) + { + ModelState.AddModelError(nameof(supervisedSelf.Signoff), "Please select a Supervisor signs off self assessment option"); + return View(supervisedSelf); + } + if (supervisedSelf.Confirm == null && supervisedSelf.SignoffText == "Yes") + { + ModelState.AddModelError(nameof(supervisedSelf.Confirm), "Please select a Supervisor or nominated supervisor confirm individual assessment option"); + return View(supervisedSelf); + } + if (supervisedSelf.SignoffText == "No") + { + var model = new ManagesupervisionViewModel(supervisedSelf); + SetManagesupervisionData(model); + return RedirectToAction("ManageSupervisionSettings", "CompetencyAssessments", new { supervisedSelf.CompetencyAssessmentId }); + } + else if (supervisedSelf.ActionName != null) + { + var data = GetManagesupervisionData(); + data.CompetencyAssessmentId = supervisedSelf.CompetencyAssessmentId; + var model = new ManagesupervisionViewModel(data.LearnerDeclaration, data.SupervisorDeclaration, supervisedSelf); + model.CompetencyAssessmentId = supervisedSelf.CompetencyAssessmentId; + SetManagesupervisionData(model); + return RedirectToAction("SupervisorSignoffDeclaration", "CompetencyAssessments", + new + { + CompetencyAssessmentId = supervisedSelf.CompetencyAssessmentId, + ActionName = "Supervisor" + }); + } + else + { + var model = new ManagesupervisionViewModel(supervisedSelf); + SetManagesupervisionData(model); + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(supervisedSelf.CompetencyAssessmentId, null); + return RedirectToAction("SupervisorSignoffDeclaration", "CompetencyAssessments", new { supervisedSelf.CompetencyAssessmentId }); + } + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisorDeclaration")] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/SupervisorDeclaration")] + public IActionResult SupervisorSignoffDeclaration(int competencyAssessmentId, string? actionName) + { + var adminId = GetAdminID(); + var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervisor Signoff Declaration", baseData); + if (result.StatusCode != 200) + return result; + var data = GetManagesupervisionData(); + if (actionName == "Supervisor") + { + data.CompetencyAssessmentId = competencyAssessmentId; + data.SupervisorDeclaration.DefaultText = this.config.GetSupervisorDefaultText(); + data.SupervisorDeclaration.ActionName = actionName; + var models = new SupervisorSignoffDeclarationViewModel(data.SupervisorDeclaration); + return View(models); + } + var model = new SupervisorSignoffDeclarationViewModel(competencyAssessmentId); + model.CompetencyAssessmentName = data.CompetencyAssessmentName; + model.CompetencyAssessmentId = competencyAssessmentId; + model.DefaultText = this.config.GetSupervisorDefaultText().Replace("{{CompetencyAssessmentName}}", model.CompetencyAssessmentName); ; + return View(model); + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisorDeclaration")] + public IActionResult SupervisorSignoffDeclaration(SupervisorSignoffDeclarationViewModel viewModel) + { + if (viewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + if (viewModel.DeclarationValue == null) + { + ModelState.AddModelError(nameof(viewModel.DeclarationValue), "Please select a declaration option"); + return View(viewModel); + } + else if (viewModel.DeclarationValue == 1 && string.IsNullOrWhiteSpace(viewModel.CustomText)) + { + ModelState.AddModelError(nameof(viewModel.CustomText), "Please enter the custom declaration text"); + return View(viewModel); + } + else if (viewModel.DeclarationValue == 1 && viewModel.CustomText.Length > 2000) + { + ModelState.AddModelError(nameof(viewModel.CustomText), "Declaration text must be 2000 characters or fewer"); + return View(viewModel); + } + + var data = GetManagesupervisionData(); + var model = new ManagesupervisionViewModel(data.LearnerDeclaration, viewModel, data.Signoff); + model.CompetencyAssessmentName = viewModel.CompetencyAssessmentName; + SetManagesupervisionData(model); + if (viewModel.ActionName != null) return RedirectToAction("LearnerSignoffDeclaration", "CompetencyAssessments", + new + { + CompetencyAssessmentId = viewModel.CompetencyAssessmentId, + ActionName = "Learner" + }); + return RedirectToAction("LearnerSignoffDeclaration", "CompetencyAssessments", new { viewModel.CompetencyAssessmentId }); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/LearnerDeclaration")] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/LearnerDeclaration")] + public IActionResult LearnerSignoffDeclaration(int competencyAssessmentId, string? actionName) + { + var adminId = GetAdminID(); + var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Learner Signoff Declaration", baseData); + if (result.StatusCode != 200) + return result; + var data = GetManagesupervisionData(); + if (actionName == "Learner") + { + data.CompetencyAssessmentId = competencyAssessmentId; + data.LearnerDeclaration.DefaultText = this.config.GetLearnerDefaultText(); + data.LearnerDeclaration.ActionName = actionName; + var models = new LearnerSignoffDeclarationViewModel(data.LearnerDeclaration); + models.CompetencyAssessmentId = competencyAssessmentId; + return View(models); + } + var model = new LearnerSignoffDeclarationViewModel(competencyAssessmentId); + model.CompetencyAssessmentName = data.CompetencyAssessmentName; + model.CompetencyAssessmentId = competencyAssessmentId; + model.DefaultText = this.config.GetLearnerDefaultText().Replace("{{CompetencyAssessmentName}}", model.CompetencyAssessmentName); + return View(model); + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/LearnerDeclaration")] + public IActionResult LearnerSignoffDeclaration(LearnerSignoffDeclarationViewModel viewModel) + { + if (viewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + if (viewModel.DeclarationValue == null) + { + ModelState.AddModelError(nameof(viewModel.DeclarationValue), "Please select a declaration option"); + return View(viewModel); + } + else if (viewModel.DeclarationValue == 1 && string.IsNullOrWhiteSpace(viewModel.CustomText)) + { + ModelState.AddModelError(nameof(viewModel.CustomText), "Please enter the custom declaration text"); + return View(viewModel); + } + else if (viewModel.DeclarationValue == 1 && viewModel.CustomText.Length > 2000) + { + ModelState.AddModelError(nameof(viewModel.CustomText), "Declaration text must be 2000 characters or fewer"); + return View(viewModel); + } + + var data = GetManagesupervisionData(); + var model = new ManagesupervisionViewModel(viewModel, data.SupervisorDeclaration, data.Signoff); + model.CompetencyAssessmentName = viewModel.CompetencyAssessmentName; + SetManagesupervisionData(model); + return RedirectToAction("ManageSupervisionSettings", "CompetencyAssessments", new { viewModel.CompetencyAssessmentId }); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisionSettings")] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/SupervisionSettings")] + public IActionResult ManageSupervisionSettings(int competencyAssessmentId, string? actionName) + { + var adminId = GetAdminID(); + var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervisor Signoff Declaration", baseData); + if (result.StatusCode != 200) + return result; + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + if (actionName == "SupervisorRoles") + { + var model = new ManagesupervisionViewModel(competencyAssessmentId, baseData.CompetencyAssessmentName, + baseData.SupervisorResultsReview, + baseData.SupervisorSelfAssessmentReview, + baseData.SignOffSupervisorStatement, + baseData.SignOffRequestorStatement, + this.config.GetLearnerDefaultText(), + this.config.GetSupervisorDefaultText()); + model.TaskCompleteChecked = competencyAssessmentTaskStatus.SupervisorRolesTaskStatus; + SetManagesupervisionData(model); + return View(model); + } + var data = GetManagesupervisionData(); + var dataModel = new ManagesupervisionViewModel(competencyAssessmentId, data, this.config.GetLearnerDefaultText(), this.config.GetSupervisorDefaultText()); + dataModel.TaskCompleteChecked = competencyAssessmentTaskStatus.SupervisorRolesTaskStatus; + return View(dataModel); + + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisionSettings")] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/SupervisionSettings")] + public IActionResult ManageSupervisionSettings(ManagesupervisionViewModel viewModel) + { + if (viewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + var data = GetManagesupervisionData(); + if (!ModelState.IsValid) + { + var model = new ManagesupervisionViewModel(data.LearnerDeclaration, data.SupervisorDeclaration, data.Signoff); + return View("ManageSupervisionSettings", model); + } + var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(data.Signoff.CompetencyAssessmentId, null); + competencyAssessmentService.UpdateSupervisorRolesTaskStatus(data.Signoff.CompetencyAssessmentId, viewModel.TaskCompleteChecked ?? false); + competencyAssessmentService.UpdateSelfAssessments(data.Signoff.CompetencyAssessmentId, + data.Signoff.Signoff, + data.Signoff.Confirm, + data.SupervisorDeclaration.DeclarationValue, + SanitizerHelper.SanitizeHtmlData(data.SupervisorDeclaration.CustomText), + data.LearnerDeclaration.DeclarationValue, + SanitizerHelper.SanitizeHtmlData(data.LearnerDeclaration.CustomText) + ); + multiPageFormService.ClearMultiPageFormData(MultiPageFormDataFeature.AddCustomWebForm("ManagesupervisionDataCWF"), TempData); + TempData.Clear(); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = viewModel.CompetencyAssessmentId }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/SendForReview")] + public IActionResult SendForReview(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var collaborators = competencyAssessmentService.GetCollaboratorsForCompetencyAssessmentId(competencyAssessmentId); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Send For Review", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var model = new SendForReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName, collaborators, competencyAssessmentBase.PublishStatusID); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/SendForReview")] + public IActionResult SendForReview(SendForReviewViewModel send) + { + var adminId = GetAdminID(); + if (!ModelState.IsValid || send.UserChecked == null || !send.UserChecked.Any()) + { + send.Collaborators = competencyAssessmentService.GetCollaboratorsForCompetencyAssessmentId(send.CompetencyAssessmentID).Where(x => x.CompetencyAssessmentRole != "Owner"); + ModelState.AddModelError("UserChecked", "You must select at least one user to send for review."); + return View(send); + } + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(send.CompetencyAssessmentID, adminId); + var result = ValidateCompetencyAssessmentAndRole(send.CompetencyAssessmentID, adminId, "Send For Review", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + foreach (var collaborator in send.UserChecked) + { + var required = send.SignOffRequiredChecked.IndexOf(collaborator) != -1; + competencyAssessmentService.InsertSelfAssessmentReview(send.CompetencyAssessmentID, collaborator, required); + frameworkNotificationService.SendReviewRequestForCompetencyAssessment(collaborator, adminId, required, false, User.GetCentreIdKnownNotNull()); + } + competencyAssessmentService.UpdateCompetencyAssessmentPublishStatus(send.CompetencyAssessmentID, 2, adminId); + var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(send.CompetencyAssessmentID, null); + if (taskStatus.ReviewTaskStatus != true) + { + competencyAssessmentService.UpdateCompetencyAssessmentReviewTaskStatus(send.CompetencyAssessmentID, false); + } + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = send.CompetencyAssessmentID }); + + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/PublishWithoutReview")] + public IActionResult PublishWithoutReview(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Publish Without Review", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var model = new PublishWithoutReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/PublishWithoutReview")] + public IActionResult PublishWithoutReview(PublishWithoutReviewViewModel publish) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(publish.CompetencyAssessmentID, adminId); + var result = ValidateCompetencyAssessmentAndRole(publish.CompetencyAssessmentID, adminId, "Publish Without Review", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + if (competencyAssessmentBase.PublishStatusID == 3) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 }); + var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(publish.CompetencyAssessmentID, null); + competencyAssessmentService.UpdateCompetencyAssessmentReviewTaskStatus(publish.CompetencyAssessmentID, true); + competencyAssessmentService.UpdateCompetencyAssessmentPublish(publish.CompetencyAssessmentID, 3, adminId, true, true); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = publish.CompetencyAssessmentID }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{selfAssessmentReviewId}/Review")] + public IActionResult SubmitReview(int competencyAssessmentId, int selfAssessmentReviewId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var selfAssessmentReview = competencyAssessmentService.GetCompetencySelfAssessmentReviewById(competencyAssessmentId, selfAssessmentReviewId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Submit Review", competencyAssessmentBase); + if (result.StatusCode != 200) return result; + if (selfAssessmentReview == null || selfAssessmentReview.SignedOff) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 }); + var model = new ViewModels.CompetencyAssessments.SubmitReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName, selfAssessmentReview); + return View(model); + } + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/{selfAssessmentReviewId}/Review")] + public IActionResult SubmitReview(SubmitReviewViewModel submit) + { + var adminId = GetAdminID(); + var centreId = GetCentreId().GetValueOrDefault(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(submit.CompetencyAssessmentID, adminId); + int? commentId = null; + var result = ValidateCompetencyAssessmentAndRole(submit.CompetencyAssessmentID, adminId, "Submit Review", competencyAssessmentBase); + if (result.StatusCode != 200) return result; + if (string.IsNullOrWhiteSpace(submit.SelfAssessmentReview.Comment) || !submit.SelfAssessmentReview.SignedOff) + { + if (string.IsNullOrWhiteSpace(submit.SelfAssessmentReview.Comment)) + { + ModelState.AddModelError( + "SelfAssessmentReview.Comment", + "Please enter a comment" + ); + } + + if (!submit.SelfAssessmentReview.SignedOff) + { + ModelState.AddModelError( + "SelfAssessmentReview.SignedOff", + "You must confirm your approval for the self-assessment to be published" + ); + } + + return View(submit); + } + commentId = competencyAssessmentService.InsertComment(submit.CompetencyAssessmentID, adminId, submit.SelfAssessmentReview.Comment, null); + competencyAssessmentService.UpdateSelfAssessmentReview(submit.CompetencyAssessmentID, submit.SelfAssessmentReview.ID, submit.SelfAssessmentReview.SignedOff, commentId); + frameworkNotificationService.SendCompetencyAssessmentsReviewOutcomeNotification(submit.SelfAssessmentReview.ID, centreId); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = submit.CompetencyAssessmentID }); + } + [HttpGet] + [Route("/CompetencyAssessments/{competencyAssessmentId}/PublishReview")] + public IActionResult PublishReview(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Publish Without Review", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + var competencyAssessmentIdReviews = competencyAssessmentService.GetCompetencySelfAssessmentReviews(competencyAssessmentId); + var model = new PublishReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName, competencyAssessmentIdReviews, competencyAssessmentBase); + return View(model); + } + public IActionResult PublishSelfAssesment(int competencyAssessmentId) + { + var adminId = GetAdminID(); + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Publish Without Review", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + if (competencyAssessmentBase.PublishStatusID == 3) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 }); + var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); + competencyAssessmentService.UpdateCompetencyAssessmentReviewTaskStatus(competencyAssessmentId, true); + competencyAssessmentService.UpdateCompetencyAssessmentPublish(competencyAssessmentId, 3, adminId, true, true); + return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = competencyAssessmentId }); + } + public IActionResult ResendRequest(int reviewId, int competencyAssessmentId, int competencyAssessmentCollaboratorId, bool required) + { + var adminId = GetAdminID(); + frameworkNotificationService.SendReviewRequestForCompetencyAssessment(competencyAssessmentCollaboratorId, adminId, required, true, User.GetCentreIdKnownNotNull()); + competencyAssessmentService.UpdateReviewRequestedDate(reviewId); + return RedirectToAction("PublishReview", new { competencyAssessmentId }); + } + public IActionResult RequestReReview(int competencyAssessmentId, int reviewId) + { + var adminId = GetAdminID(); + competencyAssessmentService.InsertCompetencySelfAssessmentReview(reviewId); + var review = competencyAssessmentService.GetSelfAssessmentReviewNotification(reviewId); + if (review == null) return StatusCode(404); + frameworkNotificationService.SendReviewRequestForCompetencyAssessment(review.SelfAssessmentCollaboratorID, adminId, review.SignOffRequired, false, User.GetCentreIdKnownNotNull()); + return RedirectToAction("PublishReview", new { competencyAssessmentId }); + } + public IActionResult RemoveRequest(int competencyAssessmentId, int reviewId) + { + competencyAssessmentService.ArchiveSelfAssessmentReviewRequest(reviewId); + return RedirectToAction("PublishReview", new { competencyAssessmentId }); + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/Preview")] + public IActionResult CompetencyAssessmentPreview(int competencyAssessmentId = 0) + { + var adminId = GetAdminID(); + var userId = (int)User.GetUserId(); + var centreId = (int)GetCentreId(); + var candidateId = User.GetCandidateIdKnownNotNull(); + var competencyAssessmentBase = new CompetencyAssessmentBase(); + + if (competencyAssessmentId <= 0) + { + return StatusCode(500); + } + competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Name", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + + if (selfAssessmentService.CanDelegateAccessSelfAssessment(userId, competencyAssessmentId, centreId)) + { + return RedirectToAction("SelfAssessment", "LearningPortal", new { selfAssessmentId = competencyAssessmentId }); + } + else + { + return RedirectToAction("PreviewConfirm", new { competencyAssessmentId }); + } + } + + [Route("/CompetencyAssessments/{competencyAssessmentId}/Preview/Confirm")] + public IActionResult PreviewConfirm(int competencyAssessmentId) + { + if (competencyAssessmentId <= 0) + { + return StatusCode(500); + } + + var adminId = GetAdminID(); + var userId = (int)User.GetUserId(); + var centreId = (int)GetCentreId(); + var centreName = centresService.GetCentreName(centreId); + var competencyAssessmentBase = new CompetencyAssessmentBase(); + + competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Preview Confirm", competencyAssessmentBase); + if (result.StatusCode != 200) + return result; + + if (selfAssessmentService.CanDelegateAccessSelfAssessment(userId, competencyAssessmentId, centreId)) + { + return StatusCode(500); + } + + var model = new CompetencyAssessmentPreviewViewModel(competencyAssessmentId, centreId, centreName); + + return View("CompetencyAssessmentPreviewConfirm", model); + } + + [HttpPost] + [Route("/CompetencyAssessments/{competencyAssessmentId}/Preview/Confirm")] + public IActionResult PreviewConfirm(CompetencyAssessmentPreviewViewModel model) + { + var adminId = GetAdminID(); + var userId = User.GetUserIdKnownNotNull(); + var centreId = (int)GetCentreId(); + var userEmail = User.GetUserPrimaryEmail(); + var candidateId = User.GetCandidateIdKnownNotNull(); + + centreSelfAssessmentsService.InsertCentreSelfAssessment(centreId, model.CompetencyAssessmentId, false); + + enrolService.EnrolOnActivitySelfAssessment(model.CompetencyAssessmentId, candidateId, 0, userEmail, 0, null, userId, centreId, adminId, true); + + return RedirectToAction("SelfAssessment", "LearningPortal", new { selfAssessmentId = model.CompetencyAssessmentId }); + } + + private void SetManagesupervisionData(ManagesupervisionViewModel data) + { + multiPageFormService.SetMultiPageFormData( + data, + MultiPageFormDataFeature.AddCustomWebForm("ManagesupervisionDataCWF"), + TempData + ); + } + private ManagesupervisionViewModel GetManagesupervisionData() + { + var data = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddCustomWebForm("ManagesupervisionDataCWF"), + TempData + ).GetAwaiter().GetResult(); + return data; + } + private void SetcompetencyAssessmentFeaturesData(CompetencyAssessmentFeaturesViewModel data) + { + multiPageFormService.SetMultiPageFormData( + data, + MultiPageFormDataFeature.AddCustomWebForm("AssessmentFeaturesDataCWF"), + TempData + ); + } + private CompetencyAssessmentFeaturesViewModel GetcompetencyAssessmentFeaturesData() + { + var data = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddCustomWebForm("AssessmentFeaturesDataCWF"), + TempData + ).GetAwaiter().GetResult(); + return data; + } + + private void SetOptionsLabelsData(OptionsLabelsViewModel data) + { + multiPageFormService.SetMultiPageFormData( + data, + MultiPageFormDataFeature.AddCustomWebForm("OptionsLabelsCWF"), + TempData + ); + } + + private OptionsLabelsViewModel GetOptionsLabelslData() + { + var data = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddCustomWebForm("OptionsLabelsCWF"), + TempData + ).GetAwaiter().GetResult(); + return data; + } + private bool ValidateStep(OptionsLabelsViewModel data, ref int step) + { + int original = step; + if (step == (int)OptionLabel.Declaration && !data.IsSupervisionSwitchedOn) step = (int)OptionLabel.Signposting; + if (step == (int)OptionLabel.Signposting && !data.IsSignpostedLearning) step = (int)OptionLabel.LinearNavigation; + if (step == (int)OptionLabel.CommentsLabel && !data.IsSupervisionSwitchedOn) step = (int)OptionLabel.Summary; + + return step != original; + } + + private StatusCodeResult ValidateCompetencyAssessmentAndRole(int competencyAssessmentId, int adminId, string pageName, CompetencyAssessmentBase? competencyAssessmentBase = null) + { + if (competencyAssessmentId > 0) + { + competencyAssessmentBase ??= competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + if (competencyAssessmentBase == null) + { + logger.LogWarning($"Failed to load {pageName} page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}"); + return StatusCode(500); + } + if (competencyAssessmentBase.UserRole < 2) + { + return StatusCode(403); + } + } + else + { + return StatusCode(500); + } + return StatusCode(200); + } + } +} diff --git a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessmentsController.cs b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessmentsController.cs new file mode 100644 index 0000000000..031ec28ec7 --- /dev/null +++ b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessmentsController.cs @@ -0,0 +1,86 @@ +namespace DigitalLearningSolutions.Web.Controllers.CompetencyAssessmentsController +{ + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.Services; + using GDS.MultiPageFormData; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Mvc; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.Logging; + + [Authorize(Policy = CustomPolicies.UserFrameworksAdminOnly)] + public partial class CompetencyAssessmentsController : Controller + { + private readonly ICompetencyAssessmentService competencyAssessmentService; + private readonly IFrameworkService frameworkService; + private readonly ICommonService commonService; + private readonly IFrameworkNotificationService frameworkNotificationService; + private readonly ISelfAssessmentNotificationService selfAssessmentNotificationService; + private readonly ISelfAssessmentService selfAssessmentService; + private readonly ICentresService centresService; + private readonly ICentreSelfAssessmentsService centreSelfAssessmentsService; + private readonly IEnrolService enrolService; + private readonly ILogger logger; + private readonly IConfiguration config; + private readonly IMultiPageFormService multiPageFormService; + public CompetencyAssessmentsController( + ICompetencyAssessmentService competencyAssessmentService, + IFrameworkService frameworkService, + ICommonService commonService, + IFrameworkNotificationService frameworkNotificationService, + ISelfAssessmentNotificationService selfAssessmentNotificationService, + ISelfAssessmentService selfAssessmentService, + ICentresService centresService, + ICentreSelfAssessmentsService centreSelfAssessmentsService, + IEnrolService enrolService, + ILogger logger, + IConfiguration config, + IMultiPageFormService multiPageFormService) + { + this.competencyAssessmentService = competencyAssessmentService; + this.frameworkService = frameworkService; + this.commonService = commonService; + this.frameworkNotificationService = frameworkNotificationService; + this.selfAssessmentNotificationService = selfAssessmentNotificationService; + this.selfAssessmentService = selfAssessmentService; + this.centresService = centresService; + this.centreSelfAssessmentsService = centreSelfAssessmentsService; + this.enrolService = enrolService; + this.logger = logger; + this.config = config; + this.multiPageFormService = multiPageFormService; + } + public IActionResult Index() + { + return View(); + } + private int? GetCentreId() + { + return User.GetCustomClaimAsInt(CustomClaimTypes.UserCentreId); + } + private int GetAdminID() + { + return User.GetCustomClaimAsRequiredInt(CustomClaimTypes.UserAdminId); + } + private bool GetIsWorkforceManager() + { + var isWorkforceManager = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceManager); + return isWorkforceManager != null && (bool)isWorkforceManager; + } + private bool GetIsWorkforceContributor() + { + var isWorkforceContributor = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceContributor); + return isWorkforceContributor != null && (bool)isWorkforceContributor; + } + private bool GetIsFrameworkDeveloper() + { + var isFrameworkDeveloper = User.GetCustomClaimAsBool(CustomClaimTypes.IsFrameworkDeveloper); + return isFrameworkDeveloper != null && (bool)isFrameworkDeveloper; + } + private bool GetIsFrameworkContributor() + { + var isFrameworkContributor = User.GetCustomClaimAsBool(CustomClaimTypes.IsFrameworkContributor); + return isFrameworkContributor != null && (bool)isFrameworkContributor; + } + } +} diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs index 01c95404e3..19a918296f 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs @@ -68,11 +68,11 @@ public IActionResult AddEditFrameworkCompetencyGroup(int frameworkId, Competency if (string.IsNullOrWhiteSpace(StringHelper.StripHtmlTags(competencyGroupBase?.Description))) { competencyGroupBase.Description = null; } if (competencyGroupBase.ID > 0) { - frameworkService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupBase.CompetencyGroupID, competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData + frameworkService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupBase.CompetencyGroupID, competencyGroupBase.Name.Trim(), SanitizerHelper.SanitizeHtmlData (competencyGroupBase.Description), adminId); return new RedirectResult(Url.Action("ViewFramework", new { tabname = "Structure", frameworkId }) + "#fcgroup-" + frameworkCompetencyGroupId.ToString()); } - var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData(competencyGroupBase.Description), adminId, frameworkId); + var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name.Trim(), SanitizerHelper.SanitizeHtmlData(competencyGroupBase.Description), adminId, frameworkId); if (newCompetencyGroupId > 0) { var newFrameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminId); @@ -159,6 +159,7 @@ public IActionResult AddEditFrameworkCompetency(int frameworkId, FrameworkCompet FrameworkCompetency = frameworkCompetency, CompetencyFlags = competencyFlags }; + ModelState.Remove("SearchableName"); return View("Developer/Competency", model); } var adminId = GetAdminId(); @@ -168,7 +169,7 @@ public IActionResult AddEditFrameworkCompetency(int frameworkId, FrameworkCompet { - frameworkService.UpdateFrameworkCompetency(frameworkCompetencyId, frameworkCompetency.Name, frameworkCompetency.Description, adminId); + frameworkService.UpdateFrameworkCompetency(frameworkCompetencyId, frameworkCompetency.Name.Trim(), frameworkCompetency.Description, adminId); frameworkService.UpdateCompetencyFlags(frameworkId, frameworkCompetency.CompetencyID, selectedFlagIds); return new RedirectResult(Url.Action("ViewFramework", new { tabname = "Structure", frameworkId, frameworkCompetencyGroupId, frameworkCompetencyId }) + "#fc-" + frameworkCompetencyId.ToString()); } @@ -215,7 +216,7 @@ public IActionResult AddDuplicateCompetency(int frameworkId, string competencyNa private IActionResult SaveCompetency(int adminId, int frameworkId, FrameworkCompetency frameworkCompetency, int frameworkCompetencyId, int? frameworkCompetencyGroupId, int[]? selectedFlagIds) { - var newCompetencyId = frameworkService.InsertCompetency(frameworkCompetency.Name, frameworkCompetency.Description, adminId); + var newCompetencyId = frameworkService.InsertCompetency(frameworkCompetency.Name.Trim(), frameworkCompetency.Description, adminId); if (newCompetencyId > 0) { var newFrameworkCompetencyId = frameworkService.InsertFrameworkCompetency(newCompetencyId, frameworkCompetencyGroupId, adminId, frameworkId); diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs index f78ef7a44f..a7729069ec 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs @@ -20,6 +20,7 @@ namespace DigitalLearningSolutions.Web.Controllers.FrameworksController using DigitalLearningSolutions.Web.ViewModels.Common; using System.Net; using DigitalLearningSolutions.Web.ServiceFilter; + using System; public partial class FrameworksController { @@ -136,18 +137,18 @@ public IActionResult StartNewFrameworkSession() MultiPageFormDataFeature.AddNewFramework, TempData ); - return RedirectToAction("CreateNewFramework", "Frameworks", new { actionname = "New" }); + return RedirectToAction("CreateNewFramework", "Frameworks", new { actionName = "New" }); } - [Route("/Frameworks/Name/{actionname}/{frameworkId}")] - [Route("/Frameworks/Name/{actionname}")] + [Route("/Frameworks/Name/{actionName}/{frameworkId}")] + [Route("/Frameworks/Name/{actionName}")] [SetSelectedTab(nameof(NavMenuTab.Frameworks))] [ResponseCache(CacheProfileName = "Never")] [TypeFilter( typeof(RedirectToErrorEmptySessionData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) } )] - public IActionResult CreateNewFramework(string actionname, int frameworkId = 0) + public IActionResult CreateNewFramework(string actionName, int frameworkId = 0) { var adminId = GetAdminId(); DetailFramework? detailFramework; @@ -173,15 +174,15 @@ public IActionResult CreateNewFramework(string actionname, int frameworkId = 0) return View("Developer/Name", detailFramework); } [HttpPost] - [Route("/Frameworks/Name/{actionname}/{frameworkId}")] - [Route("/Frameworks/Name/{actionname}")] + [Route("/Frameworks/Name/{actionName}/{frameworkId}")] + [Route("/Frameworks/Name/{actionName}")] [SetSelectedTab(nameof(NavMenuTab.Frameworks))] [ResponseCache(CacheProfileName = "Never")] [TypeFilter( typeof(RedirectToErrorEmptySessionData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) } )] - public IActionResult CreateNewFramework(DetailFramework detailFramework, string actionname, int frameworkId = 0) + public IActionResult CreateNewFramework(DetailFramework detailFramework, string actionName, int frameworkId = 0) { if (!ModelState.IsValid) { @@ -189,7 +190,7 @@ public IActionResult CreateNewFramework(DetailFramework detailFramework, string ModelState.AddModelError(nameof(BaseFramework.FrameworkName), "Please enter a valid framework name (between 3 and 255 characters)"); return View("Developer/Name", detailFramework); } - if (actionname == "New") + if (actionName == "New") { SessionNewFramework sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); sessionNewFramework.DetailFramework = detailFramework; @@ -198,7 +199,7 @@ public IActionResult CreateNewFramework(DetailFramework detailFramework, string MultiPageFormDataFeature.AddNewFramework, TempData ); - return RedirectToAction("SetNewFrameworkName", new { frameworkname = detailFramework.FrameworkName, actionname }); + return RedirectToAction("SetNewFrameworkName", new { frameworkname = detailFramework.FrameworkName, actionName }); } var adminId = GetAdminId(); var isUpdated = frameworkService.UpdateFrameworkName(detailFramework.ID, adminId, detailFramework.FrameworkName); @@ -207,10 +208,10 @@ public IActionResult CreateNewFramework(DetailFramework detailFramework, string return View("Developer/Name", detailFramework); } - [Route("/Frameworks/Similar/{actionname}")] - public IActionResult SetNewFrameworkName(string frameworkname, string actionname) + [Route("/Frameworks/Similar/{actionName}")] + public IActionResult SetNewFrameworkName(string frameworkname, string actionName) { - if (actionname == "New") + if (actionName == "New") { var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); multiPageFormService.SetMultiPageFormData( @@ -241,14 +242,14 @@ public IActionResult SetNewFrameworkName(string frameworkname, string actionname }; return View("Developer/Similar", model); } - return RedirectToAction("SaveNewFramework", "Frameworks", new { frameworkname, actionname }); + return RedirectToAction("SaveNewFramework", "Frameworks", new { frameworkname, actionName }); } - public IActionResult SaveNewFramework(string frameworkname, string actionname) + public IActionResult SaveNewFramework(string frameworkname, string actionName) { //var framework = frameworkService.CreateFramework(frameworkname, GetAdminID()); //need to create framework and move to next step. - if (actionname != "New") + if (actionName != "New") { return StatusCode(500); } @@ -258,23 +259,23 @@ public IActionResult SaveNewFramework(string frameworkname, string actionname) MultiPageFormDataFeature.AddNewFramework, TempData ); - return RedirectToAction("FrameworkDescription", "Frameworks", new { actionname }); + return RedirectToAction("FrameworkDescription", "Frameworks", new { actionName }); } - [Route("/Frameworks/Description/{actionname}/")] - [Route("/Frameworks/Description/{actionname}/{frameworkId}/")] + [Route("/Frameworks/Description/{actionName}/")] + [Route("/Frameworks/Description/{actionName}/{frameworkId}/")] [SetSelectedTab(nameof(NavMenuTab.Frameworks))] [ResponseCache(CacheProfileName = "Never")] [TypeFilter( typeof(RedirectToErrorEmptySessionData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) } )] - public IActionResult FrameworkDescription(string actionname, int frameworkId = 0) + public IActionResult FrameworkDescription(string actionName, int frameworkId = 0) { var adminId = GetAdminId(); var centreId = GetCentreId(); DetailFramework? framework; - if (actionname == "New") + if (actionName == "New") { var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); framework = sessionNewFramework.DetailFramework; @@ -298,16 +299,16 @@ public IActionResult FrameworkDescription(string actionname, int frameworkId = 0 } [HttpPost] - [Route("/Frameworks/Description/{actionname}/")] - [Route("/Frameworks/Description/{actionname}/{frameworkId}/")] + [Route("/Frameworks/Description/{actionName}/")] + [Route("/Frameworks/Description/{actionName}/{frameworkId}/")] [ResponseCache(CacheProfileName = "Never")] [TypeFilter( typeof(RedirectToErrorEmptySessionData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) } )] - public IActionResult FrameworkDescription(DetailFramework detailFramework, string actionname, int frameworkId = 0) + public IActionResult FrameworkDescription(DetailFramework detailFramework, string actionName, int frameworkId = 0) { - if (actionname == "New") + if (actionName == "New") { var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); sessionNewFramework.DetailFramework = detailFramework; @@ -316,7 +317,7 @@ public IActionResult FrameworkDescription(DetailFramework detailFramework, strin MultiPageFormDataFeature.AddNewFramework, TempData ); - return RedirectToAction("FrameworkType", "Frameworks", new { actionname }); + return RedirectToAction("FrameworkType", "Frameworks", new { actionName }); } detailFramework.Description = SanitizerHelper.SanitizeHtmlData(detailFramework.Description); frameworkService.UpdateFrameworkDescription(frameworkId, GetAdminId(), detailFramework.Description); @@ -324,20 +325,20 @@ public IActionResult FrameworkDescription(DetailFramework detailFramework, strin } - [Route("/Frameworks/Type/{actionname}/")] - [Route("/Frameworks/Type/{actionname}/{frameworkId}/")] + [Route("/Frameworks/Type/{actionName}/")] + [Route("/Frameworks/Type/{actionName}/{frameworkId}/")] [SetSelectedTab(nameof(NavMenuTab.Frameworks))] [ResponseCache(CacheProfileName = "Never")] [TypeFilter( typeof(RedirectToErrorEmptySessionData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) } )] - public IActionResult FrameworkType(string actionname, int frameworkId = 0) + public IActionResult FrameworkType(string actionName, int frameworkId = 0) { var adminId = GetAdminId(); var centreId = GetCentreId(); DetailFramework? framework; - if (actionname == "New") + if (actionName == "New") { var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); framework = sessionNewFramework.DetailFramework; @@ -361,16 +362,16 @@ public IActionResult FrameworkType(string actionname, int frameworkId = 0) } [HttpPost] - [Route("/Frameworks/Type/{actionname}/")] - [Route("/Frameworks/Type/{actionname}/{frameworkId}/")] + [Route("/Frameworks/Type/{actionName}/")] + [Route("/Frameworks/Type/{actionName}/{frameworkId}/")] [ResponseCache(CacheProfileName = "Never")] [TypeFilter( typeof(RedirectToErrorEmptySessionData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) } )] - public IActionResult FrameworkType(DetailFramework detailFramework, string actionname, int frameworkId = 0) + public IActionResult FrameworkType(DetailFramework detailFramework, string actionName, int frameworkId = 0) { - if (actionname == "New") + if (actionName == "New") { var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); sessionNewFramework.DetailFramework = detailFramework; @@ -379,26 +380,26 @@ public IActionResult FrameworkType(DetailFramework detailFramework, string actio MultiPageFormDataFeature.AddNewFramework, TempData ); - return RedirectToAction("SetNewFrameworkBrand", "Frameworks", new { actionname }); + return RedirectToAction("SetNewFrameworkBrand", "Frameworks", new { actionName }); } frameworkService.UpdateFrameworkConfig(frameworkId, GetAdminId(), detailFramework.FrameworkConfig); return RedirectToAction("ViewFramework", new { tabname = "Details", frameworkId }); } - [Route("/Frameworks/Categorise/{actionname}/")] - [Route("/Frameworks/Categorise/{actionname}/{frameworkId}/")] + [Route("/Frameworks/Categorise/{actionName}/")] + [Route("/Frameworks/Categorise/{actionName}/{frameworkId}/")] [SetSelectedTab(nameof(NavMenuTab.Frameworks))] [ResponseCache(CacheProfileName = "Never")] [TypeFilter( typeof(RedirectToErrorEmptySessionData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) } )] - public IActionResult SetNewFrameworkBrand(string actionname, int frameworkId = 0) + public IActionResult SetNewFrameworkBrand(string actionName, int frameworkId = 0) { var adminId = GetAdminId(); var centreId = GetCentreId(); DetailFramework? framework; - if (actionname == "New") + if (actionName == "New") { if (TempData[MultiPageFormDataFeature.AddNewFramework.TempDataKey] == null) { @@ -443,13 +444,13 @@ public IActionResult SetNewFrameworkBrand(string actionname, int frameworkId = 0 } [HttpPost] - [Route("/Frameworks/Categorise/{actionname}/")] - [Route("/Frameworks/Categorise/{actionname}/{frameworkId}/")] - public IActionResult SetNewFrameworkBrand(DetailFramework? detailFramework, string actionname, int frameworkId = 0) + [Route("/Frameworks/Categorise/{actionName}/")] + [Route("/Frameworks/Categorise/{actionName}/{frameworkId}/")] + public IActionResult SetNewFrameworkBrand(DetailFramework? detailFramework, string actionName, int frameworkId = 0) { var adminId = GetAdminId(); var centreId = GetCentreId(); - if (actionname != "New" && frameworkId > 0) + if (actionName != "New" && frameworkId > 0) { detailFramework = InsertBrandingCategoryTopicIfRequired(detailFramework); if (detailFramework == null) return RedirectToAction("SetNewFrameworkBrand", "Frameworks", new { frameworkId }); @@ -577,8 +578,8 @@ public IActionResult RemoveFrameworkFlag(RemoveCustomFlagConfirmationViewModel m } [HttpPost] - [Route("/Frameworks/Flags/{actionname}/{frameworkId}/{flagId}")] - public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkId, string actionname, int flagId) + [Route("/Frameworks/Flags/{actionName}/{frameworkId}/{flagId}")] + public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkId, string actionName, int flagId) { if (ModelState.IsValid) { @@ -588,7 +589,7 @@ public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkI bool nameExists = flags.Any(x => x.FlagName?.Trim().ToLower() == model.FlagName?.Trim().ToLower()); bool idExists = flags.Any(x => x.FlagId == flagId); - if (actionname == "Edit") + if (actionName == "Edit") { if (nameExists && !idExists) { @@ -615,11 +616,11 @@ public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkI } [HttpGet] - [Route("/Frameworks/Flags/{actionname:regex(Edit|New)}/{frameworkId}/{flagId}")] - public IActionResult EditFrameworkFlag(int frameworkId, string actionname, int flagId) + [Route("/Frameworks/Flags/{actionName:regex(Edit|New)}/{frameworkId}/{flagId}")] + public IActionResult EditFrameworkFlag(int frameworkId, string actionName, int flagId) { var model = new CustomFlagViewModel(); - if (actionname == "Edit") + if (actionName == "Edit") { var flag = frameworkService.GetCustomFlagsByFrameworkId(frameworkId, (int?)flagId).FirstOrDefault(); model = new CustomFlagViewModel() @@ -633,9 +634,9 @@ public IActionResult EditFrameworkFlag(int frameworkId, string actionname, int f return View("Developer/EditCustomFlag", model); } - [Route("/Frameworks/Collaborators/{actionname}/{frameworkId}/")] + [Route("/Frameworks/Collaborators/{actionName}/{frameworkId}/")] [ResponseCache(CacheProfileName = "Never")] - public IActionResult AddCollaborators(string actionname, int frameworkId, bool error = false) + public IActionResult AddCollaborators(string actionName, int frameworkId, bool error = false) { var adminId = GetAdminId(); var collaborators = frameworkService.GetCollaboratorsForFrameworkId(frameworkId); @@ -657,15 +658,15 @@ public IActionResult AddCollaborators(string actionname, int frameworkId, bool e } [HttpPost] - [Route("/Frameworks/Collaborators/{actionname}/{frameworkId}/")] - public IActionResult AddCollaborator(string actionname, string userEmail, bool canModify, int frameworkId) + [Route("/Frameworks/Collaborators/{actionName}/{frameworkId}/")] + public IActionResult AddCollaborator(string actionName, string userEmail, bool canModify, int frameworkId) { int? centreID = GetCentreId(); var collaboratorId = frameworkService.AddCollaboratorToFramework(frameworkId, userEmail, canModify, centreID); if (collaboratorId > 0) { frameworkNotificationService.SendFrameworkCollaboratorInvite(collaboratorId, GetAdminId()); - return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionname }); + return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionName }); } else { @@ -690,15 +691,15 @@ public IActionResult AddCollaborator(string actionname, string userEmail, bool c { TempData["FrameworkError"] = "User not added,Kindly try again;"; } - return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionname }); + return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionName }); } } - public IActionResult RemoveCollaborator(int frameworkId, string actionname, int id) + public IActionResult RemoveCollaborator(int frameworkId, string actionName, int id) { frameworkService.RemoveCollaboratorFromFramework(frameworkId, id); - return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionname }); + return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionName }); } [Route("/Framework/{frameworkId}/{tabname}/{frameworkCompetencyGroupId}/{frameworkCompetencyId}")] @@ -723,9 +724,9 @@ public IActionResult ViewFramework(string tabname, int frameworkId, int? framewo model.TabNavLinks = new TabsNavViewModel(FrameworkTab.Details, routeData); break; case "Structure": - model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).ToList(); + model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId, null).ToList(); model.CompetencyFlags = frameworkService.GetCompetencyFlagsByFrameworkId(frameworkId, null, selected: true); - model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId); + model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId, null); model.TabNavLinks = new TabsNavViewModel(FrameworkTab.Structure, routeData); break; case "Comments": @@ -746,9 +747,9 @@ public IActionResult PrintLayout(int frameworkId) { DetailFramework = detailFramework, }; - model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).ToList(); + model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId, null).ToList(); model.CompetencyFlags = frameworkService.GetCompetencyFlagsByFrameworkId(frameworkId, null, selected: true); - model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId); + model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId, null); return View("Developer/FrameworkPrintLayout", model); } [ResponseCache(CacheProfileName = "Never")] @@ -771,7 +772,56 @@ public IActionResult InsertFramework() detailFramework.Description = SanitizerHelper.SanitizeHtmlData(detailFramework.Description); var newFramework = frameworkService.CreateFramework(detailFramework, adminId); TempData.Clear(); - return RedirectToAction("AddCollaborators", "Frameworks", new { actionname = "New", frameworkId = newFramework.ID }); + return RedirectToAction("AddCollaborators", "Frameworks", new { actionName = "New", frameworkId = newFramework.ID }); + } + + [HttpGet] + [Route("/Frameworks/ChangeOwner/{frameworkId}")] + public IActionResult ChangeOwner(int frameworkId) + { + var framework = frameworkService.GetBaseFrameworkByFrameworkId(frameworkId, GetAdminId()); + if (framework == null) return StatusCode(404); + + var userId = User.GetUserIdKnownNotNull(); + var userAdminIds = frameworkService.GetAdminIdsForUserId(userId); + if (!userAdminIds.Contains(framework.OwnerAdminID)) + return StatusCode(403); + + var model = new ChangeOwnerViewModel + { + FrameworkId = frameworkId, + FrameworkName = framework.FrameworkName + }; + return View("Developer/ChangeOwner", model); + } + + [HttpPost] + [Route("/Frameworks/ChangeOwner/{frameworkId}")] + public IActionResult ChangeOwner(int frameworkId, ChangeOwnerViewModel model) + { + var framework = frameworkService.GetBaseFrameworkByFrameworkId(frameworkId, GetAdminId()); + model.FrameworkName = framework.FrameworkName; + + if (User.GetUserPrimaryEmail() == model.NewOwnerEmail) + { + ModelState.AddModelError(nameof(model.NewOwnerEmail), "The new owner email address cannot be the same as the current owner's email address."); + return View("Developer/ChangeOwner", model); + } + if (!ModelState.IsValid) + { + return View("Developer/ChangeOwner", model); + } + + try + { + frameworkService.ChangeFrameworkOwner(frameworkId, model.NewOwnerEmail, GetAdminId()); + return RedirectToAction("ViewFramework", new { tabname = "Details", frameworkId }); + } + catch (Exception ex) + { + ModelState.AddModelError(nameof(model.NewOwnerEmail), ex.Message); + return View("Developer/ChangeOwner", model); + } } } } diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs index e9a5304e65..8ecf417de2 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs @@ -85,7 +85,24 @@ public IActionResult SubmitFrameworkReview(int frameworkId, int reviewId, string var framework = frameworkService.GetBaseFrameworkByFrameworkId(frameworkId, adminId); if (framework.FrameworkReviewID == 0 || framework.FrameworkReviewID == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 }); int? commentId = null; - if (!string.IsNullOrWhiteSpace(comment)) commentId = frameworkService.InsertComment(frameworkId, adminId, comment, null); + if (string.IsNullOrWhiteSpace(comment)) + { + ModelState.AddModelError("comment", "Please enter comment"); + var frameworkReview = frameworkService.GetFrameworkReview(frameworkId, adminId, reviewId); + frameworkReview.SignedOff = signedOff; + var model = new SubmitReviewViewModel() + { + FrameworkId = frameworkId, + FrameworkName = framework.FrameworkName, + FrameworkReview = frameworkReview + }; + return View("Developer/SubmitReview", model); + } + else + { + commentId = frameworkService.InsertComment(frameworkId, adminId, comment, null); + } + frameworkService.SubmitFrameworkReview(frameworkId, reviewId, signedOff, commentId); frameworkNotificationService.SendReviewOutcomeNotification(reviewId, User.GetCentreIdKnownNotNull()); return RedirectToAction("ViewFramework", "Frameworks", new { frameworkId, tabname = "Structure" }); diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs index 9acf3bd6c9..d994dee7a9 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs @@ -107,6 +107,13 @@ public IActionResult ConfirmAddCompetencyLearningResourceSummary(CompetencyResou { var frameworkCompetency = frameworkService.GetFrameworkCompetencyById(model.FrameworkCompetencyId.Value); string plainTextDescription = DisplayStringHelper.RemoveMarkup(model.Description); + var competencyLearningResource = competencyLearningResourcesService.GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(frameworkCompetency.CompetencyID, model.ReferenceId); + if (competencyLearningResource.Any()) + { + ModelState.Clear(); + ModelState.AddModelError("LearningResourceExists", "This learning resource is already signposted to the selected competency."); + return View("Developer/AddCompetencyLearningResourceSummary", model); + } int competencyLearningResourceId = competencyLearningResourcesService.AddCompetencyLearningResource(model.ReferenceId, model.ResourceName, plainTextDescription, model.ResourceType, model.Link, model.SelectedCatalogue, model.Rating.Value, frameworkCompetency.CompetencyID, GetAdminId()); return RedirectToAction("StartSignpostingParametersSession", "Frameworks", new { model.FrameworkId, model.FrameworkCompetencyId, model.FrameworkCompetencyGroupId, competencyLearningResourceId }); } diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs index aa3153c81d..6c071d09c8 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs @@ -67,7 +67,6 @@ public IActionResult SelfAssessment(int selfAssessmentId) ); return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); } - selfAssessmentService.IncrementLaunchCount(selfAssessmentId, delegateUserId); selfAssessmentService.UpdateLastAccessed(selfAssessmentId, delegateUserId); var supervisors = selfAssessmentService.GetAllSupervisorsForSelfAssessmentId( @@ -75,7 +74,7 @@ public IActionResult SelfAssessment(int selfAssessmentId) delegateUserId ).ToList(); var model = new SelfAssessmentDescriptionViewModel(selfAssessment, supervisors); - + return View("SelfAssessments/SelfAssessmentDescription", model); } @@ -119,7 +118,7 @@ public IActionResult ProcessAgreed(SelfAssessmentProcessViewModel model) ); return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); } - + selfAssessmentService.MarkProgressAgreed(selfAssessmentId, delegateUserId); return RedirectToAction("SelfAssessment", new { selfAssessmentId }); @@ -675,6 +674,11 @@ public IActionResult ManageSupervisors(int selfAssessmentId) .GetOtherSupervisorsForCandidate(selfAssessmentId, delegateUserId) .Where(item => supervisors.All(s => !item.SupervisorAdminID.Equals(s.SupervisorAdminID))) .ToList(); + + if (!assessment.NonReportable) + { + suggestedSupervisors = suggestedSupervisors.Where(s => s.SupervisorAdminUserID != delegateUserId).ToList(); + } } var model = new ManageSupervisorsViewModel @@ -731,7 +735,8 @@ public IActionResult StartAddNewSupervisor(int selfAssessmentId) var sessionAddSupervisor = new SessionAddSupervisor() { SelfAssessmentID = selfAssessmentId, - SelfAssessmentName = selfAssessment.Name + SelfAssessmentName = selfAssessment.Name, + NonReportable = selfAssessment.NonReportable }; multiPageFormService.SetMultiPageFormData( @@ -784,6 +789,13 @@ public IActionResult AddNewSupervisor(int selfAssessmentId, TempData["CentreID"] = sessionAddSupervisor.CentreID; } + var supervisorUserId = User.GetUserIdKnownNotNull(); + + if (!sessionAddSupervisor.NonReportable) + { + supervisors = supervisors.Where(s => s.AdminUserID != supervisorUserId).ToList(); + } + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( new SearchOptions(searchString), new SortOptions(sortBy, sortDirection), @@ -831,6 +843,14 @@ public IActionResult GetAllSupervisors(int selfAssessmentId) { supervisors = supervisors.Where(s => s.CentreID == sessionAddSupervisor.CentreID).ToList(); } + + var supervisorUserId = User.GetUserIdKnownNotNull(); + + if (!sessionAddSupervisor.NonReportable) + { + supervisors = supervisors.Where(s => s.AdminUserID != supervisorUserId).ToList(); + } + var model = new AllSupervisorsViewModel(); model.Supervisors = supervisors; model.SupervisorAdminID = sessionAddSupervisor.SupervisorAdminId; @@ -1904,3 +1924,4 @@ private static string RenderRazorViewToString(Controller controller, string view } } } + diff --git a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfiles.cs b/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfiles.cs deleted file mode 100644 index dfcae3b8ef..0000000000 --- a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfiles.cs +++ /dev/null @@ -1,306 +0,0 @@ -namespace DigitalLearningSolutions.Web.Controllers.RoleProfilesController -{ - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using DigitalLearningSolutions.Web.ViewModels.RoleProfiles; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Extensions.Logging; - using DigitalLearningSolutions.Web.Extensions; - using DigitalLearningSolutions.Data.Models.SessionData.RoleProfiles; - using Microsoft.AspNetCore.Http; - using System; - using System.Collections.Generic; - using DigitalLearningSolutions.Data.Enums; - using DigitalLearningSolutions.Web.Attributes; - using DigitalLearningSolutions.Web.Models.Enums; - - public partial class RoleProfilesController - { - private const string CookieName = "DLSFrameworkService"; - [Route("/RoleProfiles/View/{tabname}/{page=1:int}")] - [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))] - public IActionResult ViewRoleProfiles(string tabname, string? searchString = null, - string sortBy = RoleProfileSortByOptionTexts.RoleProfileName, - string sortDirection = BaseRoleProfilesPageViewModel.AscendingText, - int page = 1 - ) - { - var adminId = GetAdminID(); - var isWorkforceManager = GetIsWorkforceManager(); - var isWorkforceContributor = GetIsWorkforceContributor(); - IEnumerable roleProfiles; - if (tabname == "All") - { - roleProfiles = roleProfileService.GetAllRoleProfiles(adminId); - } - else - { - if (!isWorkforceContributor && !isWorkforceManager) - { - return RedirectToAction("ViewRoleProfiles", "RoleProfiles", new { tabname = "All" }); - } - roleProfiles = roleProfileService.GetRoleProfilesForAdminId(adminId); - } - if (roleProfiles == null) - { - logger.LogWarning($"Attempt to display role profiles for admin {adminId} returned null."); - return StatusCode(403); - } - MyRoleProfilesViewModel myRoleProfiles; - AllRoleProfilesViewModel allRoleProfiles; - if (tabname == "Mine") - { - myRoleProfiles = new MyRoleProfilesViewModel( - roleProfiles, - searchString, - sortBy, - sortDirection, - page, - isWorkforceManager); - allRoleProfiles = new AllRoleProfilesViewModel( - new List(), - searchString, - sortBy, - sortDirection, - page - ); - } - else - { - allRoleProfiles = new AllRoleProfilesViewModel( - roleProfiles, - searchString, - sortBy, - sortDirection, - page); - myRoleProfiles = new MyRoleProfilesViewModel( - new List(), - searchString, - sortBy, - sortDirection, - page, - isWorkforceManager - ); - } - - var currentTab = tabname == "All" ? RoleProfilesTab.AllRoleProfiles : RoleProfilesTab.MyRoleProfiles; - RoleProfilesViewModel? model = new RoleProfilesViewModel( - isWorkforceManager, - isWorkforceContributor, - allRoleProfiles, - myRoleProfiles, - currentTab - ); - return View("Index", model); - } - public IActionResult StartNewRoleProfileSession() - { - var adminId = GetAdminID(); - TempData.Clear(); - var sessionNewRoleProfile = new SessionNewRoleProfile(); - if (!Request.Cookies.ContainsKey(CookieName)) - { - var id = Guid.NewGuid(); - - Response.Cookies.Append( - CookieName, - id.ToString(), - new CookieOptions - { - Expires = DateTimeOffset.UtcNow.AddDays(30) - }); - - sessionNewRoleProfile.Id = id; - } - else - { - if (Request.Cookies.TryGetValue(CookieName, out string idString)) - { - sessionNewRoleProfile.Id = Guid.Parse(idString); - } - else - { - var id = Guid.NewGuid(); - - Response.Cookies.Append( - CookieName, - id.ToString(), - new CookieOptions - { - Expires = DateTimeOffset.UtcNow.AddDays(30) - }); - sessionNewRoleProfile.Id = id; - } - } - RoleProfileBase roleProfileBase = new RoleProfileBase() - { - BrandID = 6, - OwnerAdminID = adminId, - National = true, - Public = true, - PublishStatusID = 1, - UserRole = 3 - }; - sessionNewRoleProfile.RoleProfileBase = roleProfileBase; - TempData.Set(sessionNewRoleProfile); - return RedirectToAction("RoleProfileName", "RoleProfiles", new { actionname = "New" }); - } - - [Route("/RoleProfiles/Name/{actionname}/{roleProfileId}")] - [Route("/RoleProfiles/Name/{actionname}")] - [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))] - public IActionResult RoleProfileName(string actionname, int roleProfileId = 0) - { - var adminId = GetAdminID(); - RoleProfileBase? roleProfileBase; - if (roleProfileId > 0) - { - roleProfileBase = roleProfileService.GetRoleProfileBaseById(roleProfileId, adminId); - if (roleProfileBase == null) - { - logger.LogWarning($"Failed to load name page for roleProfileId: {roleProfileId} adminId: {adminId}"); - return StatusCode(500); - } - if (roleProfileBase.UserRole < 2) - { - return StatusCode(403); - } - } - else - { - SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek(); - TempData.Set(sessionNewRoleProfile); - roleProfileBase = sessionNewRoleProfile.RoleProfileBase; - TempData.Set(sessionNewRoleProfile); - } - return View("Name", roleProfileBase); - } - - [HttpPost] - [Route("/RoleProfiles/Name/{actionname}/{roleProfileId}")] - [Route("/RoleProfiles/Name/{actionname}")] - [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))] - public IActionResult SaveProfileName(RoleProfileBase roleProfileBase, string actionname, int roleProfileId = 0) - { - if (!ModelState.IsValid) - { - ModelState.Remove(nameof(RoleProfileBase.RoleProfileName)); - ModelState.AddModelError(nameof(RoleProfileBase.RoleProfileName), "Please enter a valid role profile name (between 3 and 255 characters)"); - // do something - return View("Name", roleProfileBase); - } - else - { - if (actionname == "New") - { - var sameItems = roleProfileService.GetRoleProfileByName(roleProfileBase.RoleProfileName, GetAdminID()); - if (sameItems != null) - { - ModelState.Remove(nameof(RoleProfileBase.RoleProfileName)); - ModelState.AddModelError(nameof(RoleProfileBase.RoleProfileName), "Another role profile exists with that name. Please choose a different name."); - // do something - return View("Name", roleProfileBase); - } - SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek(); - sessionNewRoleProfile.RoleProfileBase = roleProfileBase; - TempData.Set(sessionNewRoleProfile); - return RedirectToAction("RoleProfileProfessionalGroup", "RoleProfiles", new { actionname }); - } - else - { - var adminId = GetAdminID(); - var isUpdated = roleProfileService.UpdateRoleProfileName(roleProfileBase.ID, adminId, roleProfileBase.RoleProfileName); - if (isUpdated) - { - return RedirectToAction("ViewRoleProfile", new { tabname = "Details", roleProfileId }); - } - else - { - ModelState.AddModelError(nameof(RoleProfileBase.RoleProfileName), "Another role profile exists with that name. Please choose a different name."); - // do something - return View("Name", roleProfileBase); - } - } - - } - } - - [Route("/RoleProfiles/ProfessionalGroup/{actionname}/{roleProfileId}")] - [Route("/RoleProfiles/ProfessionalGroup/{actionname}")] - [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))] - public IActionResult RoleProfileProfessionalGroup(string actionname, int roleProfileId = 0) - { - var adminId = GetAdminID(); - RoleProfileBase? roleProfileBase; - if (roleProfileId > 0) - { - roleProfileBase = roleProfileService.GetRoleProfileBaseById(roleProfileId, adminId); - if (roleProfileBase == null) - { - logger.LogWarning($"Failed to load Professional Group page for roleProfileId: {roleProfileId} adminId: {adminId}"); - return StatusCode(500); - } - if (roleProfileBase.UserRole < 2) - { - return StatusCode(403); - } - } - else - { - SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek(); - TempData.Set(sessionNewRoleProfile); - roleProfileBase = sessionNewRoleProfile.RoleProfileBase; - TempData.Set(sessionNewRoleProfile); - } - var professionalGroups = roleProfileService.GetNRPProfessionalGroups(); - var model = new ProfessionalGroupViewModel() - { - NRPProfessionalGroups = professionalGroups, - RoleProfileBase = roleProfileBase - }; - return View("ProfessionalGroup", model); - } - - [HttpPost] - [Route("/RoleProfiles/ProfessionalGroup/{actionname}/{roleProfileId}")] - [Route("/RoleProfiles/ProfessionalGroup/{actionname}")] - [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))] - public IActionResult SaveProfessionalGroup(RoleProfileBase roleProfileBase, string actionname, int roleProfileId = 0) - { - if (roleProfileBase.NRPProfessionalGroupID == null) - { - ModelState.Remove(nameof(RoleProfileBase.NRPProfessionalGroupID)); - ModelState.AddModelError(nameof(RoleProfileBase.NRPProfessionalGroupID), "Please choose a professional group" + (roleProfileId == 0 ? "or Skip this step" : "") + "."); - // do something - return View("Name", roleProfileBase); - } - if (actionname == "New") - { - SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek(); - sessionNewRoleProfile.RoleProfileBase = roleProfileBase; - TempData.Set(sessionNewRoleProfile); - return RedirectToAction("RoleProfileSubGroup", "RoleProfiles", new { actionname }); - } - else - { - var adminId = GetAdminID(); - var isUpdated = roleProfileService.UpdateRoleProfileProfessionalGroup(roleProfileBase.ID, adminId, roleProfileBase.NRPProfessionalGroupID); - if (isUpdated) - { - return RedirectToAction("RoleProfileSubGroup", "RoleProfiles", new { actionname, roleProfileId }); - } - else - { - return RedirectToAction("ViewRoleProfile", new { tabname = "Details", roleProfileId }); - } - } - } - - [Route("/RoleProfiles/SubGroup/{actionname}/{roleProfileId}")] - [Route("/RoleProfiles/SubGroup/{actionname}")] - [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))] - public IActionResult RoleProfileSubGroup(string actionname, int roleProfileId = 0) - { - return View("SubGroup"); - } - } -} diff --git a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfilesController.cs b/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfilesController.cs deleted file mode 100644 index 9a3173289f..0000000000 --- a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfilesController.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace DigitalLearningSolutions.Web.Controllers.RoleProfilesController -{ - using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.Services; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.Logging; - - [Authorize(Policy = CustomPolicies.UserFrameworksAdminOnly)] - public partial class RoleProfilesController : Controller - { - private readonly IRoleProfileService roleProfileService; - private readonly ICommonService commonService; - private readonly IFrameworkNotificationService frameworkNotificationService; - private readonly ILogger logger; - private readonly IConfiguration config; - public RoleProfilesController( - IRoleProfileService roleProfileService, - ICommonService commonService, - IFrameworkNotificationService frameworkNotificationService, - ILogger logger, - IConfiguration config) - { - this.roleProfileService = roleProfileService; - this.commonService = commonService; - this.frameworkNotificationService = frameworkNotificationService; - this.logger = logger; - this.config = config; - } - public IActionResult Index() - { - return View(); - } - private int? GetCentreId() - { - return User.GetCustomClaimAsInt(CustomClaimTypes.UserCentreId); - } - private int GetAdminID() - { - return User.GetCustomClaimAsRequiredInt(CustomClaimTypes.UserAdminId); - } - private bool GetIsWorkforceManager() - { - var isWorkforceManager = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceManager); - return isWorkforceManager != null && (bool)isWorkforceManager; - } - private bool GetIsWorkforceContributor() - { - var isWorkforceContributor = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceContributor); - return isWorkforceContributor != null && (bool)isWorkforceContributor; - } - } -} diff --git a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs index b4de042350..6a7e831d0b 100644 --- a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs +++ b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs @@ -263,16 +263,6 @@ private string IsMemberAlreadySupervisor(int adminId, return string.Empty; } - public IActionResult ConfirmSupervise(int supervisorDelegateId) - { - var adminId = GetAdminId(); - if (supervisorService.ConfirmSupervisorDelegateById(supervisorDelegateId, 0, adminId)) - { - frameworkNotificationService.SendSupervisorDelegateConfirmed(supervisorDelegateId, adminId, 0, GetCentreId()); - } - - return RedirectToAction("MyStaffList"); - } public IActionResult RemoveSupervisorDelegate() { return RedirectToAction("MyStaffList"); @@ -704,9 +694,9 @@ public IActionResult StartEnrolDelegateOnProfileAssessment(int supervisorDelegat { TempData.Clear(); - var sessionEnrolOnRoleProfile = new SessionEnrolOnRoleProfile(); + var sessionEnrolOnCompetencyAssessment = new SessionEnrolOnCompetencyAssessment(); multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); @@ -724,12 +714,12 @@ public IActionResult StartEnrolDelegateOnProfileAssessment(int supervisorDelegat )] public IActionResult EnrolDelegateOnProfileAssessment(int supervisorDelegateId) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData( + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData( MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ).GetAwaiter().GetResult(); multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); @@ -737,25 +727,25 @@ public IActionResult EnrolDelegateOnProfileAssessment(int supervisorDelegateId) var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); - var roleProfiles = supervisorService.GetAvailableRoleProfilesForDelegate( + var competencyAssessments = supervisorService.GetAvailableCompetencyAssessmentsForDelegate( (int)supervisorDelegate.DelegateUserID, GetCentreId(), loggedInAdmin.CategoryId ); var model = new EnrolDelegateOnProfileAssessmentViewModel() { - SessionEnrolOnRoleProfile = sessionEnrolOnRoleProfile, + SessionEnrolOnCompetencyAssessment = sessionEnrolOnCompetencyAssessment, SupervisorDelegateDetail = supervisorDelegate, - RoleProfiles = roleProfiles + CompetencyAssessments = competencyAssessments }; return View("EnrolDelegateOnProfileAssessment", model); } [HttpPost] [Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/Enrol/Profile")] - public IActionResult EnrolSetRoleProfile(int supervisorDelegateId, int selfAssessmentID) + public IActionResult EnrolSetCompetencyAssessment(int supervisorDelegateId, int selfAssessmentID) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData( + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData( MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ).GetAwaiter().GetResult(); @@ -766,32 +756,32 @@ public IActionResult EnrolSetRoleProfile(int supervisorDelegateId, int selfAsses { ModelState.AddModelError("selfAssessmentId", "You must select a self assessment"); multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); - var roleProfiles = supervisorService.GetAvailableRoleProfilesForDelegate( + var competencyAssessments = supervisorService.GetAvailableCompetencyAssessmentsForDelegate( (int)supervisorDelegate.DelegateUserID, GetCentreId(), loggedInAdmin.CategoryId ); var model = new EnrolDelegateOnProfileAssessmentViewModel() { - SessionEnrolOnRoleProfile = sessionEnrolOnRoleProfile, + SessionEnrolOnCompetencyAssessment = sessionEnrolOnCompetencyAssessment, SupervisorDelegateDetail = supervisorDelegate, - RoleProfiles = roleProfiles + CompetencyAssessments = competencyAssessments }; return View("EnrolDelegateOnProfileAssessment", model); } - if (sessionEnrolOnRoleProfile.SelfAssessmentID != selfAssessmentID) - sessionEnrolOnRoleProfile = new SessionEnrolOnRoleProfile(); + if (sessionEnrolOnCompetencyAssessment.SelfAssessmentID != selfAssessmentID) + sessionEnrolOnCompetencyAssessment = new SessionEnrolOnCompetencyAssessment(); - sessionEnrolOnRoleProfile.SelfAssessmentID = selfAssessmentID; + sessionEnrolOnCompetencyAssessment.SelfAssessmentID = selfAssessmentID; multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); @@ -812,7 +802,7 @@ public IActionResult EnrolSetRoleProfile(int supervisorDelegateId, int selfAsses [Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/Enrol/Confirm")] public IActionResult ConfirmRetiringSelfAssessment(int supervisorDelegateId) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData( + var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData( MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ).GetAwaiter().GetResult(); @@ -836,7 +826,7 @@ public IActionResult ConfirmRetiringSelfAssessment(int supervisorDelegateId) [Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/Enrol/Confirm")] public IActionResult ConfirmRetiringSelfAssessment(RetiringSelfAssessmentViewModel retiringSelfAssessment) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData( + var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData( MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ).GetAwaiter().GetResult(); @@ -871,25 +861,30 @@ public IActionResult ConfirmRetiringSelfAssessment(RetiringSelfAssessmentViewMod )] public IActionResult EnrolDelegateCompleteBy(int supervisorDelegateId, int? day, int? month, int? year) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData( + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData( MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ).GetAwaiter().GetResult(); + multiPageFormService.SetMultiPageFormData( + sessionEnrolOnCompetencyAssessment, + MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, + TempData + ); - var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnRoleProfile.SelfAssessmentID).RetirementDate; - if (SelfAssessmentHelper.CheckRetirementDate(retirementDate) && !sessionEnrolOnRoleProfile.ActionConfirmed) + var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID).RetirementDate; + if (CheckRetirementDate(retirementDate) && !sessionEnrolOnCompetencyAssessment.ActionConfirmed) { return RedirectToAction("ConfirmRetiringSelfAssessment", "Supervisor", new { supervisorDelegateId }); } var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); - var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID); + var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID); var model = new EnrolDelegateSetCompletByDateViewModel() { SupervisorDelegateDetail = supervisorDelegate, - RoleProfile = roleProfile, - CompleteByDate = sessionEnrolOnRoleProfile.CompleteByDate, - ActionConfirmed = sessionEnrolOnRoleProfile.ActionConfirmed + CompetencyAssessment = competencyAssessment, + CompleteByDate = sessionEnrolOnCompetencyAssessment.CompleteByDate, + ActionConfirmed = sessionEnrolOnCompetencyAssessment.ActionConfirmed }; if (day != null && month != null && year != null) { @@ -906,22 +901,34 @@ public IActionResult EnrolDelegateSetCompleteBy(int supervisorDelegateId, int da TempData["completeByDate"] = day; TempData["completeByMonth"] = month; TempData["completeByYear"] = year; - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); if (day != 0 | month != 0 | year != 0) { var validationResult = OldDateValidator.ValidateDate(day, month, year); if (!validationResult.DateValid) + { return RedirectToAction("EnrolDelegateCompleteBy", new { supervisorDelegateId, day, month, year }); + } + else + { + var completeByDate = new DateTime(year, month, day); + sessionEnrolOnCompetencyAssessment.CompleteByDate = completeByDate; + multiPageFormService.SetMultiPageFormData( + sessionEnrolOnCompetencyAssessment, + MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, + TempData + ); + } - sessionEnrolOnRoleProfile.CompleteByDate = new DateTime(year, month, day); + sessionEnrolOnCompetencyAssessment.CompleteByDate = new DateTime(year, month, day); } else { - sessionEnrolOnRoleProfile.CompleteByDate = null; + sessionEnrolOnCompetencyAssessment.CompleteByDate = null; } var supervisorRoles = - supervisorService.GetSupervisorRolesBySelfAssessmentIdForSupervisor(sessionEnrolOnRoleProfile.SelfAssessmentID.Value); + supervisorService.GetSupervisorRolesBySelfAssessmentIdForSupervisor(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value); if (supervisorRoles.Count() > 1) { TempData["navigatedFrom"] = "EnrolDelegateSupervisorRole"; @@ -933,9 +940,9 @@ public IActionResult EnrolDelegateSetCompleteBy(int supervisorDelegateId, int da } else if (supervisorRoles.Count() == 1) { - sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId = supervisorRoles.First().ID; + sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId = supervisorRoles.First().ID; multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); @@ -956,21 +963,21 @@ public IActionResult EnrolDelegateSetCompleteBy(int supervisorDelegateId, int da )] public IActionResult EnrolDelegateSupervisorRole(int supervisorDelegateId) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); - var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID); - var supervisorRoles = supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value); + var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID); + var supervisorRoles = supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value); var model = new EnrolDelegateSupervisorRoleViewModel() { SupervisorDelegateDetail = supervisorDelegate, - RoleProfile = roleProfile, - SelfAssessmentSupervisorRoleId = sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId, + CompetencyAssessment = competencyAssessment, + SelfAssessmentSupervisorRoleId = sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId, SelfAssessmentSupervisorRoles = supervisorRoles }; ViewBag.completeByDate = TempData["completeByDate"]; @@ -987,24 +994,24 @@ public IActionResult EnrolDelegateSetSupervisorRole( int selfAssessmentSupervisorRoleId ) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); if (!ModelState.IsValid) { ModelState.ClearErrorsForAllFieldsExcept("SelfAssessmentSupervisorRoleId"); var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); - var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID); + var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID); var supervisorRoles = - supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value); + supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value); model.SupervisorDelegateDetail = supervisorDelegate; - model.RoleProfile = roleProfile; + model.CompetencyAssessment = competencyAssessment; model.SelfAssessmentSupervisorRoles = supervisorRoles; return View("EnrolDelegateSupervisorRole", model); } - sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId = selfAssessmentSupervisorRoleId; + sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId = selfAssessmentSupervisorRoleId; multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); @@ -1023,39 +1030,39 @@ int selfAssessmentSupervisorRoleId )] public IActionResult EnrolDelegateSummary(int supervisorDelegateId) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); - - var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnRoleProfile.SelfAssessmentID).RetirementDate; - if (SelfAssessmentHelper.CheckRetirementDate(retirementDate) && !sessionEnrolOnRoleProfile.ActionConfirmed) + + var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID).RetirementDate; + if (CheckRetirementDate(retirementDate) && !sessionEnrolOnCompetencyAssessment.ActionConfirmed) { return RedirectToAction("ConfirmRetiringSelfAssessment", "Supervisor", new { supervisorDelegateId }); } var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); - var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID); - var supervisorRoleName = (sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId == null + var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID); + var supervisorRoleName = (sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId == null ? "Supervisor" : supervisorService - .GetSupervisorRoleById(sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId.Value).RoleName); - var supervisorRoleCount = (sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId == null + .GetSupervisorRoleById(sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId.Value).RoleName); + var supervisorRoleCount = (sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId == null ? 0 : supervisorService - .GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value).Count()); - var allowSupervisorRoleSelection = (sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId == null + .GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value).Count()); + var allowSupervisorRoleSelection = (sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId == null ? false : supervisorService - .GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value).FirstOrDefault().AllowSupervisorRoleSelection); + .GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value).FirstOrDefault().AllowSupervisorRoleSelection); var model = new EnrolDelegateSummaryViewModel() { SupervisorDelegateDetail = supervisorDelegate, - RoleProfile = roleProfile, + CompetencyAssessment = competencyAssessment, SupervisorRoleName = supervisorRoleName, - CompleteByDate = sessionEnrolOnRoleProfile.CompleteByDate, + CompleteByDate = sessionEnrolOnCompetencyAssessment.CompleteByDate, SupervisorRoleCount = supervisorRoleCount, AllowSupervisorRoleSelection = allowSupervisorRoleSelection }; @@ -1063,16 +1070,16 @@ public IActionResult EnrolDelegateSummary(int supervisorDelegateId) ViewBag.completeByMonth = TempData["completeByMonth"]; ViewBag.completeByYear = TempData["completeByYear"]; ViewBag.navigatedFrom = TempData["navigatedFrom"]; - ViewBag.actionConfirmed = sessionEnrolOnRoleProfile.ActionConfirmed; + ViewBag.actionConfirmed = sessionEnrolOnCompetencyAssessment.ActionConfirmed; return View("EnrolDelegateSummary", model); } public IActionResult EnrolDelegateConfirm(int delegateUserId, int supervisorDelegateId) { - var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); - var selfAssessmentId = sessionEnrolOnRoleProfile.SelfAssessmentID; - var completeByDate = sessionEnrolOnRoleProfile.CompleteByDate; - var selfAssessmentSupervisorRoleId = sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId; + var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult(); + var selfAssessmentId = sessionEnrolOnCompetencyAssessment.SelfAssessmentID; + var completeByDate = sessionEnrolOnCompetencyAssessment.CompleteByDate; + var selfAssessmentSupervisorRoleId = sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId; var loggedInUserId = User.GetUserId(); var candidateAssessmentId = supervisorService.EnrolDelegateOnAssessment( delegateUserId, @@ -1104,7 +1111,7 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele { var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); - var roleProfile = supervisorService.GetRoleProfileById(selfAssessmentId); + var competencyAssessment = supervisorService.GetCompetencyAssessmentById(selfAssessmentId); var supervisorRoles = supervisorService.GetSupervisorRolesBySelfAssessmentIdForSupervisor(selfAssessmentId); if (supervisorRoles.Any() && supervisorRoles.Count() > 1) @@ -1112,7 +1119,7 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele var model = new EnrolDelegateSupervisorRoleViewModel() { SupervisorDelegateDetail = supervisorDelegate, - RoleProfile = roleProfile, + CompetencyAssessment = competencyAssessment, SelfAssessmentSupervisorRoleId = null, SelfAssessmentSupervisorRoles = supervisorRoles }; @@ -1132,25 +1139,25 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele } } - var sessionEnrolOnRoleProfile = new SessionEnrolOnRoleProfile() + var sessionEnrolOnCompetencyAssessment = new SessionEnrolOnCompetencyAssessment() { SelfAssessmentID = supervisorRoles.FirstOrDefault() != null ? supervisorRoles.FirstOrDefault().SelfAssessmentID : selfAssessmentId, SelfAssessmentSupervisorRoleId = supervisorRoles.FirstOrDefault() != null ? supervisorRoles.FirstOrDefault().ID : 0 }; multiPageFormService.SetMultiPageFormData( - sessionEnrolOnRoleProfile, + sessionEnrolOnCompetencyAssessment, MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData ); var supervisorRoleName = supervisorRoles.FirstOrDefault() != null ? supervisorRoles.FirstOrDefault().RoleName : ""; var model = new EnrolDelegateSummaryViewModel { - RoleProfile = roleProfile, + CompetencyAssessment = competencyAssessment, SupervisorDelegateDetail = supervisorDelegate, SupervisorRoleName = supervisorRoleName }; - return View("SelectDelegateSupervisorRoleSummary", new Tuple(model, sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId)); + return View("SelectDelegateSupervisorRoleSummary", new Tuple(model, sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId)); } @@ -1160,7 +1167,7 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele [HttpPost] public IActionResult QuickAddSupervisor(EnrolDelegateSupervisorRoleViewModel supervisorRole, int selfAssessmentId, int supervisorDelegateId, int delegateUserId) { - var roleProfile = supervisorService.GetRoleProfileById(selfAssessmentId); + var competencyAssessment = supervisorService.GetCompetencyAssessmentById(selfAssessmentId); var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); if (supervisorRole.SelfAssessmentSupervisorRoleId == null) @@ -1169,7 +1176,7 @@ public IActionResult QuickAddSupervisor(EnrolDelegateSupervisorRoleViewModel sup var model = new EnrolDelegateSupervisorRoleViewModel() { SupervisorDelegateDetail = supervisorDelegate, - RoleProfile = roleProfile, + CompetencyAssessment = competencyAssessment, SelfAssessmentSupervisorRoleId = null, SelfAssessmentSupervisorRoles = supervisorRoles }; @@ -1180,7 +1187,7 @@ public IActionResult QuickAddSupervisor(EnrolDelegateSupervisorRoleViewModel sup var model = new EnrolDelegateSummaryViewModel { - RoleProfile = roleProfile, + CompetencyAssessment = competencyAssessment, SupervisorDelegateDetail = supervisorDelegate, SupervisorRoleName = supervisorRole.SelfAssessmentSupervisorRoleId == null ? "Supervisor" : supervisorService.GetSupervisorRoleById((int)supervisorRole.SelfAssessmentSupervisorRoleId).RoleName, @@ -1199,12 +1206,12 @@ public IActionResult QuickAddSupervisorConfirm(int? selfAssessmentSupervisorRole var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0); if (!selfAssessmentSupervisorRoleId.HasValue) { - var roleProfile = supervisorService.GetRoleProfileById(selfAssessmentId); + var competencyAssessment = supervisorService.GetCompetencyAssessmentById(selfAssessmentId); var supervisorRoles = supervisorService.GetSupervisorRolesForSelfAssessment(selfAssessmentId); var model = new EnrolDelegateSupervisorRoleViewModel() { SupervisorDelegateDetail = supervisorDelegate, - RoleProfile = roleProfile, + CompetencyAssessment = competencyAssessment, SelfAssessmentSupervisorRoleId = null, SelfAssessmentSupervisorRoles = supervisorRoles }; @@ -1587,5 +1594,14 @@ private static string RenderRazorViewToString(Controller controller, string view return sw.GetStringBuilder().ToString(); } } + private bool CheckRetirementDate(DateTime? date) + { + if (date == null) + return false; + + DateTime retirementOffsetDate = DateTime.Today.AddDays(14); + DateTime today = DateTime.Today; + return (date >= today && date <= retirementOffsetDate); + } } } diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs index 5a59a12086..483c75ef3a 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs @@ -16,8 +16,8 @@ using Microsoft.FeatureManagement; using Microsoft.FeatureManagement.Mvc; using System; + using System.Linq; using System.Threading.Tasks; - [FeatureGate(FeatureFlags.RefactoredTrackingSystem)] [Authorize(Policy = CustomPolicies.UserCentreAdmin)] [SetDlsSubApplication(nameof(DlsSubApplication.TrackingSystem))] @@ -33,6 +33,7 @@ public class SelfAssessmentReportsController : Controller private readonly string workbookName; private readonly string viewName; private readonly ISelfAssessmentService selfAssessmentService; + private readonly ICentreSelfAssessmentsService centreSelfAssessmentsService; private readonly IFeatureManager featureManager; public SelfAssessmentReportsController( ISelfAssessmentReportService selfAssessmentReportService, @@ -40,6 +41,7 @@ public SelfAssessmentReportsController( IClockUtility clockUtility, IConfiguration config, ISelfAssessmentService selfAssessmentService, + ICentreSelfAssessmentsService centreSelfAssessmentsService, IFeatureManager featureManager ) { @@ -51,6 +53,7 @@ IFeatureManager featureManager workbookName = config.GetTableauWorkbookName(); viewName = config.GetTableauViewName(); this.selfAssessmentService = selfAssessmentService; + this.centreSelfAssessmentsService = centreSelfAssessmentsService; this.featureManager = featureManager; } [Route("/TrackingSystem/Centre/Reports/SelfAssessments")] @@ -59,10 +62,12 @@ public async Task IndexAsync() var centreId = User.GetCentreId(); var adminCategoryId = User.GetAdminCategoryId(); var categoryId = this.selfAssessmentService.GetSelfAssessmentCategoryId(1); + var selfAssessments = centreSelfAssessmentsService.GetCentreSelfAssessments(centreId.Value); + var dSATreportIsPublish = selfAssessments.Any(x => x.SelfAssessmentId == 1); var tableauFlag = await featureManager.IsEnabledAsync(FeatureFlags.TableauSelfAssessmentDashboards); var tableauQueryOverride = string.Equals(Request.Query["tableaulink"], "true", StringComparison.OrdinalIgnoreCase); var showTableauLink = tableauFlag || tableauQueryOverride; - var model = new SelfAssessmentReportsViewModel(selfAssessmentReportService.GetSelfAssessmentsForReportList((int)centreId, adminCategoryId), adminCategoryId, categoryId, showTableauLink); + var model = new SelfAssessmentReportsViewModel(selfAssessmentReportService.GetSelfAssessmentsForReportList((int)centreId, adminCategoryId), adminCategoryId, categoryId, dSATreportIsPublish, showTableauLink); return View(model); } [HttpGet] diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs index f806ab1991..3352b34d74 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs @@ -244,6 +244,7 @@ public IActionResult Index( saDelegate.LastAccessed = DateHelper.GetLocalDateTime(saDelegate.LastAccessed); saDelegate.SubmittedDate = DateHelper.GetLocalDateTime(saDelegate.SubmittedDate); saDelegate.RemovedDate = DateHelper.GetLocalDateTime(saDelegate.RemovedDate); + saDelegate.SignedOff = saDelegate.SignedOff is null ? null : DateHelper.GetLocalDateTime(saDelegate.SignedOff.Value); } } diff --git a/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs b/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs index 6dccdc57c7..413cd48339 100644 --- a/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs @@ -54,7 +54,7 @@ bool MatchesSearch(Competency c) => // Define reusable filter checks var filterChecks = new Dictionary> { - [SelfAssessmentCompetencyFilter.RequiresSelfAssessment] = c => c.AssessmentQuestions.Any(q => q.ResultId == null), + [SelfAssessmentCompetencyFilter.RequiresSelfAssessment] = c => c.AssessmentQuestions.Any(q => q.ResultId == null || q.Result == null), [SelfAssessmentCompetencyFilter.SelfAssessed] = c => c.AssessmentQuestions.Any(q => q.ResultId != null && q.Requested == null && q.SignedOff == null), [SelfAssessmentCompetencyFilter.ConfirmationRequested] = c => c.AssessmentQuestions.Any(q => q.Verified == null && q.Requested != null), [SelfAssessmentCompetencyFilter.ConfirmationRejected] = c => c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff != true), diff --git a/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs b/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs index 5014a2d93c..9f6e6a571b 100644 --- a/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs @@ -71,6 +71,21 @@ public static string GetPluralitySuffix(int number) { return number == 1 ? string.Empty : "s"; } + public static string PluraliseStringIfRequired(string input, int number) + { + if (number == 1) + { + return input; + } + else if (input.EndsWith("y")) + { + return input.Substring(0, input.Length - 1) + "ies"; + } + else + { + return input + "s"; + } + } public static string? ReplaceNonAlphaNumericSpaceChars(string? input, string replacement) { diff --git a/DigitalLearningSolutions.Web/Helpers/MaxOptionalCompetenciesAttribute.cs b/DigitalLearningSolutions.Web/Helpers/MaxOptionalCompetenciesAttribute.cs new file mode 100644 index 0000000000..3c01dad383 --- /dev/null +++ b/DigitalLearningSolutions.Web/Helpers/MaxOptionalCompetenciesAttribute.cs @@ -0,0 +1,38 @@ +namespace DigitalLearningSolutions.Web.Helpers; +using System; +using System.ComponentModel.DataAnnotations; + +public class MaxOptionalCountAttribute : ValidationAttribute +{ + private readonly string _maxProperty; + + public MaxOptionalCountAttribute(string maxProperty) + { + _maxProperty = maxProperty; + } + + protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) + { + var number = value as int?; + if (number == null) + { + return ValidationResult.Success; + } + + // Get the max property value (OptionalCompetenciesCount) + var maxProp = validationContext.ObjectType.GetProperty(_maxProperty); + if (maxProp == null) + { + return new ValidationResult($"Unknown property {_maxProperty}"); + } + + var maxValue = (int)maxProp.GetValue(validationContext.ObjectInstance)!; + + if (number < 0 || number > maxValue) + { + return new ValidationResult($"Must be between 0 and {maxValue}."); + } + + return ValidationResult.Success; + } +} diff --git a/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs b/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs index 461d7ae46b..130c8d52b6 100644 --- a/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.Helpers { - using System.Text.RegularExpressions; using Microsoft.AspNetCore.Mvc.ModelBinding; + using System.Text.RegularExpressions; public class ProfessionalRegistrationNumberHelper { @@ -48,13 +48,17 @@ public static void ValidateProfessionalRegistrationNumber( ); } - const string pattern = @"^[a-z\d-]+$"; + const string pattern = @"^(\d{7}|[A-Za-z]{1,2}\d{6}|\d{4,8}|P?\d{5,6}|[C|P]\d{6}|[A-Za-z]?\d{5,6}|L\d{4,6}|\d{2}-[A-Za-z\d]{4,5})$"; var rg = new Regex(pattern, RegexOptions.IgnoreCase); if (!rg.Match(prn).Success) { modelState.AddModelError( "ProfessionalRegistrationNumber", - "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed" + "Invalid professional registration number format. " + + "Valid formats include: 7 digits (e.g., 1234567), 1–2 letters followed by 6 digits (e.g., AB123456), " + + "4–8 digits, an optional 'P' plus 5–6 digits, 'C' or 'P' plus 6 digits, " + + "an optional letter plus 5–6 digits, 'L' plus 4–6 digits, " + + "or 2 digits followed by a hyphen and 4–5 alphanumeric characters (e.g., 12-AB123)." ); } } diff --git a/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs b/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs index 381be134dd..9b3dc5acf8 100644 --- a/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using DigitalLearningSolutions.Data.Helpers; - using DigitalLearningSolutions.Data.Models.RoleProfiles; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; using FuzzySharp; using FuzzySharp.SimilarityRatio; using FuzzySharp.SimilarityRatio.Scorer.StrategySensitive; @@ -29,8 +29,8 @@ public static class SearchHelper "profile", "job", "role", }; - public static IEnumerable FilterRoleProfiles( - IEnumerable roleProfiles, + public static IEnumerable FilterCompetencyAssessments( + IEnumerable competencyAssessments, string? searchString, int minMatchScore, bool stripStopWords @@ -38,7 +38,7 @@ bool stripStopWords { if (searchString == null) { - return roleProfiles; + return competencyAssessments; } if (stripStopWords) @@ -46,16 +46,16 @@ bool stripStopWords searchString = CleanSearchedWords(searchString); } - var query = new RoleProfile + var query = new CompetencyAssessment { - RoleProfileName = searchString.ToLower(), + CompetencyAssessmentName = searchString.ToLower(), }; if (stripStopWords) { var results = Process.ExtractSorted( query, - roleProfiles, - roleProfile => roleProfile.RoleProfileName.ToLower(), + competencyAssessments, + competencyAssessment => competencyAssessment.CompetencyAssessmentName.ToLower(), ScorerCache.Get(), minMatchScore ); @@ -65,8 +65,8 @@ bool stripStopWords { var results = Process.ExtractSorted( query, - roleProfiles, - roleProfile => roleProfile.RoleProfileName.ToLower(), + competencyAssessments, + competencyAssessment => competencyAssessment.CompetencyAssessmentName.ToLower(), ScorerCache.Get(), minMatchScore ); diff --git a/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs b/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs index e630a9ea9b..2a4cd083b8 100644 --- a/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Linq; using DigitalLearningSolutions.Data.Helpers; - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using DigitalLearningSolutions.Web.ViewModels.RoleProfiles; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments; /// /// This is the older version of the SortingHelper. For future search/sort implementations we should @@ -13,36 +13,36 @@ /// public static class SortingHelper { - public static IEnumerable SortRoleProfileItems( - IEnumerable roleProfiles, + public static IEnumerable SortCompetencyAssessmentItems( + IEnumerable competencyAssessments, string sortBy, string sortDirection ) { return sortBy switch { - RoleProfileSortByOptionTexts.RoleProfileName => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.RoleProfileName) - : roleProfiles.OrderBy(roleProfile => roleProfile.RoleProfileName), - RoleProfileSortByOptionTexts.RoleProfileOwner => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Owner) - : roleProfiles.OrderBy(roleProfile => roleProfile.Owner), - RoleProfileSortByOptionTexts.RoleProfileCreatedDate => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.CreatedDate) - : roleProfiles.OrderBy(roleProfile => roleProfile.CreatedDate), - RoleProfileSortByOptionTexts.RoleProfilePublishStatus => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.PublishStatusID) - : roleProfiles.OrderBy(roleProfile => roleProfile.PublishStatusID), - RoleProfileSortByOptionTexts.RoleProfileBrand => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Brand) - : roleProfiles.OrderBy(roleProfile => roleProfile.Brand), - RoleProfileSortByOptionTexts.RoleProfileNationalRoleProfile => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPRole) - : roleProfiles.OrderBy(roleProfile => roleProfile.NRPRole), - RoleProfileSortByOptionTexts.RoleProfileNationalRoleGroup => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPProfessionalGroup) - : roleProfiles.OrderBy(roleProfile => roleProfile.NRPProfessionalGroup), - _ => roleProfiles + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentName => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText + ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.CompetencyAssessmentName) + : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.CompetencyAssessmentName), + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentOwner => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText + ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.Owner) + : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.Owner), + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentCreatedDate => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText + ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.CreatedDate) + : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.CreatedDate), + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentPublishStatus => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText + ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.PublishStatusID) + : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.PublishStatusID), + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentBrand => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText + ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.Brand) + : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.Brand), + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentNationalCompetencyAssessment => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText + ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.NRPRole) + : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.NRPRole), + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentNationalRoleGroup => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText + ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.NRPProfessionalGroup) + : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.NRPProfessionalGroup), + _ => competencyAssessments }; } } diff --git a/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs b/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs index 58dfd9e95e..de2ddb814e 100644 --- a/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs @@ -53,7 +53,7 @@ private static void ApplyResponseStatusFilters(ref IEnumerable compe (f == (int)SelfAssessmentCompetencyFilter.ConfirmationRejected && c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff != true)) || (f == (int)SelfAssessmentCompetencyFilter.PendingConfirmation && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Verified == null && q.Requested != null && q.UserIsVerifier == false)) || (f == (int)SelfAssessmentCompetencyFilter.AwaitingConfirmation && c.AssessmentQuestions.Any(q => q.Verified == null && q.Requested != null && q.UserIsVerifier == true)) || - (f == (int)SelfAssessmentCompetencyFilter.RequiresSelfAssessment && c.AssessmentQuestions.Any(q => q.ResultId == null)) || + (f == (int)SelfAssessmentCompetencyFilter.RequiresSelfAssessment && c.AssessmentQuestions.Any(q => q.ResultId == null || q.Result == null)) || (f == (int)SelfAssessmentCompetencyFilter.SelfAssessed && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Requested == null && q.SignedOff == null)) ); diff --git a/DigitalLearningSolutions.Web/Models/Enums/CompetencyAssessmentsTab.cs b/DigitalLearningSolutions.Web/Models/Enums/CompetencyAssessmentsTab.cs new file mode 100644 index 0000000000..c77a640c7e --- /dev/null +++ b/DigitalLearningSolutions.Web/Models/Enums/CompetencyAssessmentsTab.cs @@ -0,0 +1,39 @@ +namespace DigitalLearningSolutions.Web.Models.Enums +{ + using System.Collections.Generic; + + public class CompetencyAssessmentsTab : BaseTabEnumeration + { + public static CompetencyAssessmentsTab MyCompetencyAssessments = new CompetencyAssessmentsTab( + 1, + nameof(MyCompetencyAssessments), + "CompetencyAssessments", + "ViewCompetencyAssessments", + "My Competency Assessments", + new Dictionary { { "tabName", "Mine" } } + ); + + public static CompetencyAssessmentsTab AllCompetencyAssessments = new CompetencyAssessmentsTab( + 2, + nameof(AllCompetencyAssessments), + "CompetencyAssessments", + "ViewCompetencyAssessments", + "All Competency Assessments", + new Dictionary { { "tabName", "All" } } + ); + + private CompetencyAssessmentsTab( + int id, + string name, + string controller, + string action, + string linkText, + Dictionary staticRouteData + ) : base(id, name, controller, action, linkText, staticRouteData) { } + + public override IEnumerable GetAllTabs() + { + return GetAll(); + } + } +} diff --git a/DigitalLearningSolutions.Web/Models/Enums/RoleProfilesTab.cs b/DigitalLearningSolutions.Web/Models/Enums/RoleProfilesTab.cs deleted file mode 100644 index 3d599ce5d0..0000000000 --- a/DigitalLearningSolutions.Web/Models/Enums/RoleProfilesTab.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace DigitalLearningSolutions.Web.Models.Enums -{ - using System.Collections.Generic; - - public class RoleProfilesTab : BaseTabEnumeration - { - public static RoleProfilesTab MyRoleProfiles = new RoleProfilesTab( - 1, - nameof(MyRoleProfiles), - "RoleProfiles", - "ViewRoleProfiles", - "My Role Profiles", - new Dictionary { { "tabName", "Mine" } } - ); - - public static RoleProfilesTab AllRoleProfiles = new RoleProfilesTab( - 2, - nameof(AllRoleProfiles), - "RoleProfiles", - "ViewRoleProfiles", - "All Role Profiles", - new Dictionary { { "tabName", "All" } } - ); - - private RoleProfilesTab( - int id, - string name, - string controller, - string action, - string linkText, - Dictionary staticRouteData - ) : base(id, name, controller, action, linkText, staticRouteData) { } - - public override IEnumerable GetAllTabs() - { - return GetAll(); - } - } -} diff --git a/DigitalLearningSolutions.Web/Models/GroupedCompetencyWithAssessmentRoleRequirements.cs b/DigitalLearningSolutions.Web/Models/GroupedCompetencyWithAssessmentRoleRequirements.cs new file mode 100644 index 0000000000..ae4daa4e7d --- /dev/null +++ b/DigitalLearningSolutions.Web/Models/GroupedCompetencyWithAssessmentRoleRequirements.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; + +namespace DigitalLearningSolutions.Web.Models +{ + public class GroupedCompetencyWithAssessmentRoleRequirements + { + public int CompetencyGroupID { get; set; } = 0; + public string GroupName { get; set; } = "Ungrouped"; + public List Competencies { get; set; } = new(); + } + + public class CompetencyModel + { + public int CompetencyID { get; set; } + public string? Name { get; set; } + public string? Description { get; set; } + public bool? Optional { get; set; } + public List Questions { get; set; } = new(); + } + + public class AssessmentQuestionModel + { + public int AssessmentQuestionID { get; set; } + public string? Question { get; set; } + public string? InputTypeName { get; set; } + public bool Required { get; set; } + public List Responses { get; set; } = new(); + } + public class ResponseModel + { + public int? ResponseValue { get; set; } + public string? ResponseLabel { get; set; } + public int? LevelRAG { get; set; } + } + +} + diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts index 805db86f09..06d5c7e0f0 100644 --- a/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts +++ b/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts @@ -10,8 +10,11 @@ function brandChanged() { if (style === 'block') { tb.value = ''; tb.required = true; + tb.setCustomValidity(""); tb.focus(); } else { + tb.value = ""; + tb.setCustomValidity(""); tb.required = false; } } @@ -27,8 +30,11 @@ function categoryChanged() { if (style === 'block') { tb.value = ''; tb.required = true; + tb.setCustomValidity(""); tb.focus(); } else { + tb.value = ""; + tb.setCustomValidity(""); tb.required = false; } } @@ -44,8 +50,41 @@ function topicChanged() { if (style === 'block') { tb.value = ''; tb.required = true; + tb.setCustomValidity(""); tb.focus(); } else { + tb.value = ""; + tb.setCustomValidity(""); tb.required = false; } } +const form = document.querySelector("form") as HTMLFormElement; +const bfield = document.getElementById('brand-field'); +const cfield = document.getElementById('category-field'); +const tfield = document.getElementById('topic-field'); +function wireClearOnInput(input: HTMLInputElement) { + input.addEventListener("input", () => { + input.setCustomValidity(""); + }); +} + +if (bfield) wireClearOnInput(bfield); +if (cfield) wireClearOnInput(cfield); +if (tfield) wireClearOnInput(tfield); + +form.addEventListener("submit", (e: Event) => { + [ + { el: bfield, msg: "Please enter a valid brand." }, + { el: cfield, msg: "Please enter a valid category." }, + { el: tfield, msg: "Please enter a valid topic." } + ].forEach(f => { + if (f.el.required && !f.el.value.trim()) { + e.preventDefault(); + f.el.setCustomValidity(f.msg); + f.el.reportValidity(); + f.el.focus(); + } else { + f.el.setCustomValidity(""); + } + }); +}); diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts index 129697c0af..3952d26df3 100644 --- a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts +++ b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts @@ -70,14 +70,19 @@ if (jodited === false) { const clean = DOMPurify.sanitize(editor.editor.innerHTML); editor.editor.innerHTML = clean; }); + + document.addEventListener('DOMContentLoaded', () => { + removeWaveErrors(); + removeDevToolsIssues(); + }); + + // ** Start* for jodit editor error (display red outline, focus on summary error text click) **** const textarea = document.querySelector('.nhsuk-textarea.html-editor.nhsuk-input--error') as HTMLTextAreaElement | null; if (textarea) { const editorDiv = document.querySelector('.jodit-container.jodit.jodit_theme_default.jodit-wysiwyg_mode') as HTMLDivElement | null; editorDiv?.classList.add('jodit-container', 'jodit', 'jodit_theme_default', 'jodit-wysiwyg_mode', 'jodit-error'); } - const summary = document.querySelector('.nhsuk-list.nhsuk-error-summary__list') as HTMLDivElement | null; - if (summary) { summary.addEventListener('click', (e: Event) => { if (textarea) { @@ -96,5 +101,70 @@ if (jodited === false) { } }); } + // ** End* for jodit editor error (display red outline, focus on summary error text click) **** + } +} + +function removeWaveErrors() { + const input = Array.from(document.querySelectorAll('input[tab-index="-1"]')) + .find((el) => el.style.width === '0px' && el.style.height === '0px' + && el.style.position === 'absolute' && el.style.visibility === 'hidden'); + + if (input) { + input.setAttribute('aria-label', 'Hidden input for accessibility'); + input.setAttribute('title', 'HiddenInput'); + } + + const observer = new MutationObserver((mutations, obs) => { + const textarea = document.querySelector('.ace_text-input') as HTMLTextAreaElement | null; + if (textarea) { + textarea.setAttribute('aria-label', 'ace_text-input'); + obs.disconnect(); + } + }); + observer.observe(document.body, { + childList: true, + subtree: true, + }); +} +function removeDevToolsIssues() { + // set role = 'list' to toolbar + const toolbarbox = document.querySelector('.jodit-toolbar__box') as HTMLElement | null; + if (toolbarbox) { + toolbarbox.setAttribute('role', 'list'); + } + // set role = 'list' to statusbar + const statusbar = document.querySelector('.jodit-xpath') as HTMLElement | null; + if (statusbar) { + statusbar.setAttribute('role', 'list'); } + document.querySelectorAll('.jodit-toolbar-button__trigger').forEach((el) => { + el.removeAttribute('role'); + }); + // observer to detect role='trigger' and remove role + const observer = new MutationObserver(() => { + document.querySelectorAll('.jodit-toolbar-button__trigger').forEach((el) => { + el.removeAttribute('role'); + }); + }); + const target = document.querySelector('.jodit-toolbar__box'); + if (target) { + observer.observe(target, { subtree: true, childList: true }); + } + + // observer to detect iframe and set title + const observer2 = new MutationObserver(() => { + const hiddenIframe = Array.from(document.querySelectorAll('iframe')).find((iframe) => { + const rect = iframe.getBoundingClientRect(); + return rect.width === 0 && rect.height === 0 && (iframe.src === 'about:blank' || iframe.getAttribute('src') === 'about:blank'); + }); + if (hiddenIframe) { + hiddenIframe.setAttribute('title', 'Hidden iframe'); + observer2.disconnect(); // Stop observing once found + } + }); + observer2.observe(document.body, { + childList: true, + subtree: true, + }); } diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/selectoptionalcompetencies.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/selectoptionalcompetencies.ts new file mode 100644 index 0000000000..3df001ea91 --- /dev/null +++ b/DigitalLearningSolutions.Web/Scripts/frameworks/selectoptionalcompetencies.ts @@ -0,0 +1,36 @@ +document.addEventListener('DOMContentLoaded', () => { + const groups = document.querySelectorAll('.nhsuk-checkboxes'); + + groups.forEach((group) => { + const groupToggle = group.querySelector('input[name="GroupIds"]'); + if (!groupToggle) return; + + // All individual competency checkboxes in the group + const childCheckboxes = group.querySelectorAll( + 'input[name="SelectedCompetencyIds"]', + ); + + const updateState = () => { + const isChecked = groupToggle.checked; + + childCheckboxes.forEach((cb) => { + if (isChecked) { + // eslint-disable-next-line no-param-reassign + cb.checked = true; // force selected + // eslint-disable-next-line no-param-reassign + cb.disabled = true; // lock them + } else { + // eslint-disable-next-line no-param-reassign + cb.disabled = false; // re-enable when group is unchecked + // optional: leave cb.checked unchanged + } + }); + }; + + // Run when the group checkbox changes + groupToggle.addEventListener('change', updateState); + + // Also run at page load in case some are pre-checked server-side + updateState(); + }); +}); diff --git a/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs b/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs index 1a50a20ae6..ef5a5e36a0 100644 --- a/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs +++ b/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs @@ -34,7 +34,7 @@ public void OnActionExecuting(ActionExecutingContext context) return; } - if (context.ActionArguments.ContainsKey("actionname") && context.ActionArguments["actionname"].ToString() == "Edit") + if (context.ActionArguments.ContainsKey("actionName") && context.ActionArguments["actionName"].ToString() == "Edit") { return; } diff --git a/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs b/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs index 67e11614a0..9c480ebca7 100644 --- a/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs +++ b/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs @@ -49,7 +49,7 @@ public void OnActionExecuting(ActionExecutingContext context) context.Result = new RedirectToActionResult("StatusCode", "LearningSolutions", new { code = 403 }); return; } - + if (!selfAssessment.IncludeLearnerDeclarationPrompt) return; var actionName = context.RouteData.Values["action"]?.ToString(); if (actionName == "AgreeSelfAssessmentProcess" || actionName == "ProcessAgreed") { diff --git a/DigitalLearningSolutions.Web/Services/CompetencyAssessmentService.cs b/DigitalLearningSolutions.Web/Services/CompetencyAssessmentService.cs new file mode 100644 index 0000000000..ab2bffac1b --- /dev/null +++ b/DigitalLearningSolutions.Web/Services/CompetencyAssessmentService.cs @@ -0,0 +1,593 @@ +using DigitalLearningSolutions.Data.DataServices; +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; +using DigitalLearningSolutions.Web.Models; +using DigitalLearningSolutions.Web.ViewModels.Frameworks; +using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Centre.Reports; +using DocumentFormat.OpenXml.Drawing.Charts; +using System.Collections.Generic; +using System.Linq; + +namespace DigitalLearningSolutions.Web.Services +{ + public interface ICompetencyAssessmentService + { + //GET DATA + IEnumerable GetAllCompetencyAssessments(int adminId); + + IEnumerable GetCompetencyAssessmentsForAdminId(int adminId); + + CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId); + + CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId); + CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId); + + IEnumerable GetNRPProfessionalGroups(); + IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID); + IEnumerable GetNRPRoles(int? nRPSubGroupID); + + CompetencyAssessmentTaskStatus GetCompetencyAssessmentTaskStatus(int assessmentId, int? frameworkId); + int[] GetLinkedFrameworkIds(int assessmentId); + int? GetPrimaryLinkedFrameworkId(int assessmentId); + + IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId); + IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId); + + bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId); + + int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId); + CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId); + int? GetSelfAssessmentStructure(int competencyAssessmentId); + List GetGroupedCompetencyWithAssessmentRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId); + int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId); + IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId); + SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId); + SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId); + + //UPDATE DATA + bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName); + bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId); + bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus); + bool UpdateCompetencyAssessmentDescription(int assessmentId, int adminId, string description); + bool UpdateCompetencyAssessmentBranding(int assessmentId, int adminId, int brandID, int categoryID); + bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus); + bool UpdateCompetencyAssessmentVocabulary(int assessmentId, int adminId, string vocabulary); + bool UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus); + bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus); + bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateSelectCompetenciesTaskStatus(int competencyAssessmentId, bool taskStatus, bool? previousStatus); + bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus); + bool UpdateCompetencyAssessmentOptions( + bool includeLearnerDeclarationPrompt, + bool includesSignposting, + bool linearNavigation, + bool useDescriptionExpanders, + string? questionLabelText, + string? reviewerCommentsLabelText, + int competencyAssessmentId, int adminId); + bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus); + void MoveCompetencyInSelfAssessment(int competencyAssessmentId, + int competencyId, + string direction + ); + void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, + int groupId, + string direction + ); + bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus, + bool workingGroupStatus, bool AllframeworkCompetenciesStatus); + void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId); + bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds); + void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies); + void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string? manageOptionalCompetenciesPrompt); + bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId); + void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters); + int UpdateAssessmentQuestionRoleRequirementsForSelfAssessment(int assessmentId, int assessmentQuestionId, Dictionary responseRoleRequirements); + int UpdateCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, Dictionary responseRoleRequirements); + bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked); + bool UpdateSelfAssessments(int competencyAssessmentId, + int? signoff, + int? confirm, + int? supervisorDeclarationValue, + string? supervisorCustomText, + int? leanerDeclarationValue, + string? leanerCustomText + ); + void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId); + void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub); + void ArchiveSelfAssessmentReviewRequest(int reviewId); + void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId); + void UpdateReviewRequestedDate(int reviewId); + bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus); + //INSERT DATA + int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName, int? frameworkId); + bool InsertSelfAssessmentFramework(int adminId, int assessmentId, int frameworkId); + int GetCompetencyCountByFrameworkId(int competencyAssessmentId, int frameworkId); + bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId); + bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId); + bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId); + void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId); + void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required); + int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId); + int InsertCompetencySelfAssessmentReview(int reviewId); + //DELETE DATA + bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId); + bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId); + bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId); + IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId); + int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID); + void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id); + CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId); + bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId); + } + public class CompetencyAssessmentService : ICompetencyAssessmentService + { + private readonly ICompetencyAssessmentDataService competencyAssessmentDataService; + private readonly IFrameworkDataService frameworkDataService; + public CompetencyAssessmentService(ICompetencyAssessmentDataService competencyAssessmentDataService, IFrameworkDataService frameworkDataService) + { + this.competencyAssessmentDataService = competencyAssessmentDataService; + this.frameworkDataService = frameworkDataService; + } + public IEnumerable GetAllCompetencyAssessments(int adminId) + { + return competencyAssessmentDataService.GetAllCompetencyAssessments(adminId); + } + + public IEnumerable GetNRPProfessionalGroups() + { + return competencyAssessmentDataService.GetNRPProfessionalGroups(); + } + + public CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId) + { + return competencyAssessmentDataService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + } + + public CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId) + { + return competencyAssessmentDataService.GetCompetencyAssessmentBaseByName(competencyAssessmentName, adminId); + } + + public IEnumerable GetCompetencyAssessmentsForAdminId(int adminId) + { + return competencyAssessmentDataService.GetCompetencyAssessmentsForAdminId(adminId); + } + public int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName, int? frameworkId) + { + var assessmentId = competencyAssessmentDataService.InsertCompetencyAssessment(adminId, centreId, competencyAssessmentName); + if (assessmentId > 0 && frameworkId != null) + { + var framework = frameworkDataService.GetBrandedFrameworkByFrameworkId((int)frameworkId, adminId); + if (framework != null) + { + competencyAssessmentDataService.InsertSelfAssessmentFramework(adminId, assessmentId, framework.ID); + competencyAssessmentDataService.UpdateCompetencyAssessmentDescription(adminId, assessmentId, framework.Description); + competencyAssessmentDataService.UpdateCompetencyAssessmentBranding(assessmentId, adminId, (int)framework.BrandID, (int)framework.CategoryID); + competencyAssessmentDataService.UpdateCompetencyAssessmentVocabulary(assessmentId, adminId, framework.Vocabulary); + } + } + return assessmentId; + } + + public bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentName(competencyAssessmentId, adminId, competencyAssessmentName); + } + + public bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId) + { + return competencyAssessmentDataService.UpdateCompetencyRoleProfileLinks(competencyAssessmentId, adminId, professionalGroupId, subGroupId, roleId); + } + public CompetencyAssessmentTaskStatus GetCompetencyAssessmentTaskStatus(int assessmentId, int? frameworkId) + { + return competencyAssessmentDataService.GetOrInsertAndReturnAssessmentTaskStatus(assessmentId, (frameworkId != null)); + } + public bool UpdateCompetencyAssessmentDescription(int competencyAssessmentId, int adminId, string description) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentDescription(competencyAssessmentId, adminId, description); + } + public bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus) + { + return competencyAssessmentDataService.UpdateIntroductoryTextTaskStatus(assessmentId, taskStatus); + } + + public CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId) + { + return competencyAssessmentDataService.GetCompetencyAssessmentById(competencyAssessmentId, adminId); + } + + public bool UpdateCompetencyAssessmentBranding(int assessmentId, int adminId, int brandID, int categoryID) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentBranding(assessmentId, adminId, brandID, categoryID); + } + + public bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus) + { + return competencyAssessmentDataService.UpdateBrandingTaskStatus(assessmentId, taskStatus); + } + + bool ICompetencyAssessmentService.UpdateCompetencyAssessmentVocabulary(int assessmentId, int adminId, string vocabulary) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentVocabulary(assessmentId, adminId, vocabulary); + } + + bool ICompetencyAssessmentService.UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus) + { + return competencyAssessmentDataService.UpdateVocabularyTaskStatus(assessmentId, taskStatus); + } + + public IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID) + { + return competencyAssessmentDataService.GetNRPSubGroups(nRPProfessionalGroupID); + } + + public IEnumerable GetNRPRoles(int? nRPSubGroupID) + { + return competencyAssessmentDataService.GetNRPRoles(nRPSubGroupID); + } + + public bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus) + { + return competencyAssessmentDataService.UpdateRoleProfileLinksTaskStatus(assessmentId, taskStatus); + } + + public int[] GetLinkedFrameworkIds(int assessmentId) + { + return competencyAssessmentDataService.GetLinkedFrameworkIds(assessmentId); + } + + public bool InsertSelfAssessmentFramework(int adminId, int assessmentId, int frameworkId) + { + return competencyAssessmentDataService.InsertSelfAssessmentFramework(adminId, assessmentId, frameworkId); + } + + public bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + return competencyAssessmentDataService.UpdateFrameworkLinksTaskStatus(assessmentId, taskStatus, previousStatus); + } + + public int? GetPrimaryLinkedFrameworkId(int assessmentId) + { + return competencyAssessmentDataService.GetPrimaryLinkedFrameworkId(assessmentId); + } + + public bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId) + { + return competencyAssessmentDataService.RemoveSelfAssessmentFramework(assessmentId, frameworkId, adminId); + } + + public int GetCompetencyCountByFrameworkId(int competencyAssessmentId, int frameworkId) + { + return competencyAssessmentDataService.GetCompetencyCountByFrameworkId(competencyAssessmentId, frameworkId); + } + + public bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId) + { + UpdateSelectCompetenciesTaskStatus(competencyAssessmentId, false, true); + UpdateOptionalCompetenciesTaskStatus(competencyAssessmentId, false, true); + UpdateRoleRequirementsTaskStatus(competencyAssessmentId, false, true); + return competencyAssessmentDataService.RemoveFrameworkCompetenciesFromAssessment(competencyAssessmentId, frameworkId); + } + + public bool UpdateSelectCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + return competencyAssessmentDataService.UpdateSelectCompetenciesTaskStatus(assessmentId, taskStatus, previousStatus); + } + + public bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + return competencyAssessmentDataService.UpdateOptionalCompetenciesTaskStatus(assessmentId, taskStatus, previousStatus); + } + + public bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + return competencyAssessmentDataService.UpdateRoleRequirementsTaskStatus(assessmentId, taskStatus, previousStatus); + } + public bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + return competencyAssessmentDataService.UpdateWorkingGroupTaskStatus(assessmentId, taskStatus, previousStatus); + } + public bool UpdateCompetencyAssessmentOptions( + bool includeLearnerDeclarationPrompt, + bool includesSignposting, + bool linearNavigation, + bool useDescriptionExpanders, + string? questionLabelText, + string? reviewerCommentsLabelText, + int competencyAssessmentId, int adminId) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentOptions( + includeLearnerDeclarationPrompt, + includesSignposting, + linearNavigation, + useDescriptionExpanders, + questionLabelText, + reviewerCommentsLabelText, + competencyAssessmentId, adminId); + } + public IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId) + { + return competencyAssessmentDataService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId); + } + + public IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId) + { + return competencyAssessmentDataService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId); + } + + public int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId) + { + return competencyAssessmentDataService.GetLinkedFrameworkCompetencyIds(competencyAssessmentId, frameworkId); + } + + public bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId) + { + return competencyAssessmentDataService.InsertCompetenciesIntoAssessmentFromFramework(selectedCompetencyIds, frameworkId, competencyAssessmentId); + } + + public bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId) + { + return competencyAssessmentDataService.RemoveCompetencyFromAssessment(competencyAssessmentId, competencyId); + } + public bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId) + { + return competencyAssessmentDataService.RemoveCompetencyGroupFromAssessment(competencyAssessmentId, competencyGroupId); + } + + public void MoveCompetencyInSelfAssessment(int competencyAssessmentId, int competencyId, string direction) + { + competencyAssessmentDataService.MoveCompetencyInSelfAssessment(competencyAssessmentId, competencyId, direction); + } + + public void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, int groupId, string direction) + { + competencyAssessmentDataService.MoveCompetencyGroupInSelfAssessment(competencyAssessmentId, groupId, direction); + } + public bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus, + bool workingGroupStatus, bool AllframeworkCompetenciesStatus) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentFeaturesTaskStatus(id, descriptionStatus, providerandCategoryStatus, vocabularyStatus, + workingGroupStatus, AllframeworkCompetenciesStatus); + } + public bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentRoleRequirementsTaskStatus(assessmentId, taskStatus); + } + public CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId) + { + return competencyAssessmentDataService.GetCompetencyAssessmentFeaturesTaskStatus(competencyAssessmentId); + } + public bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId) + { + return competencyAssessmentDataService.InsertSelfAssessmentGroupedCompetencies(selfAssessmentId, frameworkId); + } + public bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId) + { + return competencyAssessmentDataService.InsertSelfAssessmentUngroupedCompetencies(selfAssessmentId, frameworkId); + } + public void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId) + { + competencyAssessmentDataService.UpdateSelfAssessmentFromFramework(selfAssessmentId, frameworkId); + } + public bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId) + { + return competencyAssessmentDataService.UpdatePrimaryFrameworkCompetencies(assessmentId, frameworkId); + } + + public int? GetSelfAssessmentStructure(int competencyAssessmentId) + { + return competencyAssessmentDataService.GetSelfAssessmentStructure(competencyAssessmentId); + } + + public bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds) + { + return competencyAssessmentDataService.UpdateOptionalCompetenciesInAssessment(selfAssessmentId, groupIds, selectedStructureIds); + } + + public void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies) + { + competencyAssessmentDataService.UpdateMinimumOptionalCompetencies(selfAssessmentId, minimumOptionalCompetecies); + } + public void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string? manageOptionalCompetenciesPrompt) + { + manageOptionalCompetenciesPrompt = SanitizerHelper.SanitizeHtmlData(manageOptionalCompetenciesPrompt); + if (StringHelper.StripHtmlTags(manageOptionalCompetenciesPrompt) == "") + { + manageOptionalCompetenciesPrompt = null; + } + competencyAssessmentDataService.UpdateManageOptionalCompetenciesPrompt(selfAssessmentId, manageOptionalCompetenciesPrompt); + } + public IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId) + { + return competencyAssessmentDataService.GetCollaboratorsForCompetencyAssessmentId(competencyAssessmentId); + } + public int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID) + { + return competencyAssessmentDataService.AddCollaboratorToCompetencyAssessment(competencyAssessmentId, userEmail, canModify, centreID); + } + public void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id) + { + competencyAssessmentDataService.RemoveCollaboratorFromCompetencyAssessment(competencyAssessmentId, id); + } + public CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId) + { + return competencyAssessmentDataService.GetCollaboratorNotification(id, invitedByAdminId); + + } + public bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId) + { + return competencyAssessmentDataService.HasCompetencyWithSignpostedLearning(competencyAssessmentId); + } + public bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentOptionsTaskStatus(assessmentId, taskStatus); + } + + public List GetGroupedCompetencyWithAssessmentRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId) + { + var competencyWithAssessmentQuestionRoleRequirements = competencyAssessmentDataService.GetCompetencyWithAssessmentQuestionRoleRequirements(competencyAssessmentId, competencyId, assessmentQuestionId).ToList(); + return [.. competencyWithAssessmentQuestionRoleRequirements + .GroupBy(x => new + { + x.CompetencyGroupID, + x.GroupName + }) + .Select(group => new GroupedCompetencyWithAssessmentRoleRequirements + { + CompetencyGroupID = group.Key.CompetencyGroupID ?? 0, + GroupName = group.Key.GroupName ?? "Ungrouped", + + Competencies = [.. group + .GroupBy(c => new + { + c.CompetencyID, + c.Competency, + c.CompetencyDescription, + c.Optional + }) + .Select(comp => new CompetencyModel + { + CompetencyID = comp.Key.CompetencyID, + Name = comp.Key.Competency, + Description = comp.Key.CompetencyDescription, + Optional = comp.Key.Optional, + + Questions = [.. comp + .GroupBy(q => new + { + q.AssessmentQuestionID, + q.Question, + q.InputTypeName, + q.Required + }) + .Select(q => new AssessmentQuestionModel + { + AssessmentQuestionID = q.Key.AssessmentQuestionID, + Question = q.Key.Question, + InputTypeName = q.Key.InputTypeName, + Required = q.Key.Required, + Responses = [.. q + .Where(r => r.ResponseValue.HasValue) + .Select(r => new ResponseModel + { + ResponseValue = r.ResponseValue, + ResponseLabel = r.Response, + LevelRAG = r.LevelRAG + }) + .DistinctBy(r => r.ResponseValue)] + })] + })] + })]; + } + + public void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters) + { + competencyAssessmentDataService.UpdateRoleRequirementsFlags(assessmentId, enforceRoleRequirementsForSignOff, includeRequirementsFilters); + } + + public int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId) + { + return competencyAssessmentDataService.GetCountOfAsssessmentQuestionInCompetencyAssessment(competencyAssessmentId, assessmentQuestionId); + } + + public int UpdateAssessmentQuestionRoleRequirementsForSelfAssessment(int assessmentId, int assessmentQuestionId, Dictionary responseRoleRequirements) + { + int rowCount = 0; + foreach (var responseRoleRequirement in responseRoleRequirements) + { + competencyAssessmentDataService.DeleteCompetencyAssessmentQuestionRoleRequirement(assessmentId, null, assessmentQuestionId, responseRoleRequirement.Key); + if (responseRoleRequirement.Value != null) + { + rowCount += competencyAssessmentDataService.InsertAssessmentQuestionRoleRequirementForSelfAssessment(assessmentId, assessmentQuestionId, responseRoleRequirement.Key, responseRoleRequirement.Value); + } + } + return rowCount; + } + + public int UpdateCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, Dictionary responseRoleRequirements) + { + int rowCount = 0; + foreach (var responseRoleRequirement in responseRoleRequirements) + { + competencyAssessmentDataService.DeleteCompetencyAssessmentQuestionRoleRequirement(assessmentId, competencyId, assessmentQuestionId, responseRoleRequirement.Key); + if (responseRoleRequirement.Value != null) + { + rowCount += competencyAssessmentDataService.InsertCompetencyAssessmentQuestionRoleRequirement(assessmentId, competencyId, assessmentQuestionId, responseRoleRequirement.Key, responseRoleRequirement.Value); + } + } + return rowCount; + } + public void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId) + { + competencyAssessmentDataService.InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(selfAssessmentId, frameworkId); + } + public bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked) + { + return competencyAssessmentDataService.UpdateSupervisorRolesTaskStatus(competencyAssessmentId, taskCompleteChecked); + } + public bool UpdateSelfAssessments(int competencyAssessmentId, + int? signoff, + int? confirm, + int? supervisorDeclarationValue, + string? supervisorCustomText, + int? leanerDeclarationValue, + string? leanerCustomText + ) + { + return competencyAssessmentDataService.UpdateSelfAssessments(competencyAssessmentId, signoff, confirm, + supervisorDeclarationValue, supervisorCustomText, leanerDeclarationValue, leanerCustomText); + } + public void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId) + { + competencyAssessmentDataService.UpdateCompetencyAssessmentPublishStatus(competencyAssessmentId, status, adminId); + } + public void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required) + { + competencyAssessmentDataService.InsertSelfAssessmentReview(competencyAssessmentId, selfAssessmentCollaboratorID, required); + } + public void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub) + { + competencyAssessmentDataService.UpdateCompetencyAssessmentPublish(competencyAssessmentId, status, adminId, national, pub); + } + public IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId) + { + return competencyAssessmentDataService.GetCompetencySelfAssessmentReviews(competencyAssessmentId); + } + public void ArchiveSelfAssessmentReviewRequest(int reviewId) + { + competencyAssessmentDataService.ArchiveSelfAssessmentReviewRequest(reviewId); + } + public void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId) + { + competencyAssessmentDataService.UpdateSelfAssessmentReview(selfAssessmentID, reviewId, signedOff, commentId); + } + public int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId) + { + return competencyAssessmentDataService.InsertComment(selfAssessmentID, adminId, comment, replyToCommentId); + } + public SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId) + { + return competencyAssessmentDataService.GetCompetencySelfAssessmentReviewById(competencyAssessmentId, selfAssessmentReviewId); + } + public SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId) + { + return competencyAssessmentDataService.GetSelfAssessmentReviewNotification(reviewId); + } + public void UpdateReviewRequestedDate(int reviewId) + { + competencyAssessmentDataService.UpdateReviewRequestedDate(reviewId); + } + public int InsertCompetencySelfAssessmentReview(int reviewId) + { + return competencyAssessmentDataService.InsertCompetencySelfAssessmentReview(reviewId); + } + public bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus) + { + return competencyAssessmentDataService.UpdateCompetencyAssessmentReviewTaskStatus(assessmentId, taskStatus); + } + } +} diff --git a/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs b/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs index 3f9a8b295c..525d3e87e0 100644 --- a/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs +++ b/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs @@ -13,6 +13,7 @@ public interface ICompetencyLearningResourcesService IEnumerable GetCompetencyResourceAssessmentQuestionParameters(IEnumerable competencyLearningResourceIds); int AddCompetencyLearningResource(int resourceRefID, string originalResourceName, string description, string resourceType, string link, string catalogue, decimal rating, int competencyID, int adminId); + IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId); } public class CompetencyLearningResourcesService : ICompetencyLearningResourcesService { @@ -40,5 +41,9 @@ public IEnumerable GetCompetencyR { return competencyLearningResourcesDataService.GetCompetencyResourceAssessmentQuestionParameters(competencyLearningResourceIds); } + public IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId) + { + return competencyLearningResourcesDataService.GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(competencyId, referenceId); + } } } diff --git a/DigitalLearningSolutions.Web/Services/DelegateDownloadFileService.cs b/DigitalLearningSolutions.Web/Services/DelegateDownloadFileService.cs index f0bba93403..4c7c7d27ed 100644 --- a/DigitalLearningSolutions.Web/Services/DelegateDownloadFileService.cs +++ b/DigitalLearningSolutions.Web/Services/DelegateDownloadFileService.cs @@ -35,7 +35,8 @@ public class DelegateDownloadFileService : IDelegateDownloadFileService private const string LastName = "Last name"; private const string FirstName = "First name"; private const string DelegateId = "ID"; - private const string Email = "Email"; + private const string PrimaryEmail = "PrimaryEmail"; + private const string CentreEmail = "CentreEmail"; private const string ProfessionalRegistrationNumber = "Professional Registration Number"; private const string JobGroup = "Job group"; private const string RegisteredDate = "Registered"; @@ -330,7 +331,8 @@ DataTable dataTable new DataColumn(LastName), new DataColumn(FirstName), new DataColumn(DelegateId), - new DataColumn(Email), + new DataColumn(PrimaryEmail), + new DataColumn(CentreEmail), new DataColumn(ProfessionalRegistrationNumber), new DataColumn(JobGroup), new DataColumn(RegisteredDate), @@ -369,7 +371,8 @@ CentreRegistrationPrompts registrationPrompts row[LastName] = delegateRecord.LastName; row[FirstName] = delegateRecord.FirstName; row[DelegateId] = delegateRecord.CandidateNumber; - row[Email] = delegateRecord.EmailAddress; + row[CentreEmail] = delegateRecord.Email; + row[PrimaryEmail] = Guid.TryParse(delegateRecord.PrimaryEmail, out _) ? null : delegateRecord.PrimaryEmail; row[ProfessionalRegistrationNumber] = PrnHelper.GetPrnDisplayString( delegateRecord.HasBeenPromptedForPrn, delegateRecord.ProfessionalRegistrationNumber diff --git a/DigitalLearningSolutions.Web/Services/EnrolService.cs b/DigitalLearningSolutions.Web/Services/EnrolService.cs index 9da38c11a9..d2d20c0584 100644 --- a/DigitalLearningSolutions.Web/Services/EnrolService.cs +++ b/DigitalLearningSolutions.Web/Services/EnrolService.cs @@ -43,7 +43,8 @@ int EnrolOnActivitySelfAssessment( DateTime? completeByDate, int delegateUserId, int centreId, - int? enrolledByAdminId + int? enrolledByAdminId, + bool isNonReportable = false ); } public class EnrolService : IEnrolService @@ -184,9 +185,9 @@ by the system because a previous course completion has expired.

return new Email(EnrolEmailSubject, body, emailAddress); } - public int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail, int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId) + public int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail, int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId, bool isNonReportable = false) { - return courseDataService.EnrolOnActivitySelfAssessment(selfAssessmentId, candidateId, supervisorId, adminEmail, selfAssessmentSupervisorRoleId, completeByDate, delegateUserId, centreId, enrolledByAdminId); + return courseDataService.EnrolOnActivitySelfAssessment(selfAssessmentId, candidateId, supervisorId, adminEmail, selfAssessmentSupervisorRoleId, completeByDate, delegateUserId, centreId, enrolledByAdminId, isNonReportable); } } } diff --git a/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs b/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs index 092f38c15d..d63447a265 100644 --- a/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs +++ b/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs @@ -16,7 +16,6 @@ public interface IFrameworkNotificationService void SendReviewRequest(int id, int invitedByAdminId, bool required, bool reminder, int centreId); void SendReviewOutcomeNotification(int reviewId, int centreId); void SendSupervisorDelegateInvite(int supervisorDelegateId, int adminId, int centreId); - void SendSupervisorDelegateConfirmed(int superviseDelegateId, int adminId, int delegateUserId, int centreId); void SendSupervisorResultReviewed(int adminId, int supervisorDelegateId, int candidateAssessmentId, int resultId, int centreId); void SendSupervisorEnroledDelegate(int adminId, int supervisorDelegateId, int candidateAssessmentId, DateTime? completeByDate, int centreId); void SendReminderDelegateSelfAssessment(int adminId, int supervisorDelegateId, int candidateAssessmentId, int centreId); @@ -26,6 +25,8 @@ public interface IFrameworkNotificationService void SendSignOffRequest(int candidateAssessmentSupervisorId, int selfAssessmentID, int delegateUserId, int centreId); void SendProfileAssessmentSignedOff(int supervisorDelegateId, int candidateAssessmentId, string? supervisorComments, bool signedOff, int adminId, int centreId); void SendSupervisorDelegateReminder(int supervisorDelegateId, int adminId, int centreId); + void SendReviewRequestForCompetencyAssessment(int id, int invitedByAdminId, bool required, bool reminder, int centreId); + void SendCompetencyAssessmentsReviewOutcomeNotification(int reviewId, int centreId); } public class FrameworkNotificationService : IFrameworkNotificationService @@ -34,7 +35,7 @@ public class FrameworkNotificationService : IFrameworkNotificationService private readonly IConfigDataService configDataService; private readonly IEmailService emailService; private readonly IFrameworkService frameworkService; - private readonly IRoleProfileService roleProfileService; + private readonly ICompetencyAssessmentService competencyAssessmentService; private readonly ISupervisorService supervisorService; private readonly ISelfAssessmentDataService selfAssessmentDataService; private readonly ICentresDataService centresDataService; @@ -42,7 +43,7 @@ public FrameworkNotificationService( IFrameworkService frameworkService, IConfigDataService configDataService, IEmailService emailService, - IRoleProfileService roleProfileService, + ICompetencyAssessmentService competencyAssessmentService, ISupervisorService supervisorService, ISelfAssessmentDataService selfAssessmentDataService, ICentresDataService centresDataService @@ -51,7 +52,7 @@ ICentresDataService centresDataService this.frameworkService = frameworkService; this.configDataService = configDataService; this.emailService = emailService; - this.roleProfileService = roleProfileService; + this.competencyAssessmentService = competencyAssessmentService; this.supervisorService = supervisorService; this.selfAssessmentDataService = selfAssessmentDataService; this.centresDataService = centresDataService; @@ -141,12 +142,38 @@ public void SendReviewRequest(int id, int invitedByAdminId, bool required, bool }; emailService.SendEmail(new Email(emailSubjectLine, builder, collaboratorNotification.UserEmail, collaboratorNotification.InvitedByEmail)); } + public void SendReviewRequestForCompetencyAssessment(int id, int invitedByAdminId, bool required, bool reminder, int centreId) + { + string centreName = GetCentreName(centreId); + var collaboratorNotification = competencyAssessmentService.GetCollaboratorNotification(id, invitedByAdminId); + if (collaboratorNotification == null) + { + throw new NotificationDataException($"No record found when trying to fetch collaboratorNotification Data. id: {id}, invitedByAdminId: {invitedByAdminId}"); + } + var competencyAssessmentUrl = GetCompetencyAssessmentUrl(collaboratorNotification.SelfAssessmentID, "Review"); + string emailSubjectLine = (reminder ? " REMINDER: " : "") + "Competency Assessment Review Request - Digital Learning Solutions"; + string signOffRequired = required ? "You are required to sign-off this competency assessment before it can be published." : "You are not required to sign-off this competency assessment before it is published."; + var builder = new BodyBuilder + { + TextBody = $@"Dear colleague, + You have been requested to review the competency assessment, {collaboratorNotification?.CompetencyAssessmentName}, by {collaboratorNotification?.InvitedByName} ({collaboratorNotification?.InvitedByEmail}) ({centreName}). + To review the competency assessment, visit this url: {competencyAssessmentUrl}. Click the Review competency assessment button to submit your review and, if appropriate, sign-off the competency assessment. {signOffRequired}. You will need to be registered on the Digital Learning Solutions platform to review the competency assessment.", + HtmlBody = $@"

Dear colleague,

You have been requested to review the competency assessment, {collaboratorNotification?.CompetencyAssessmentName}, by {collaboratorNotification?.InvitedByName} ({centreName}).

Click here to review the competency assessment. Click the Review competency assessment button to submit your review and, if appropriate, sign-off the competency assessment.

{signOffRequired}

You will need to be registered on the Digital Learning Solutions platform to view the competency assessment.

" + }; + emailService.SendEmail(new Email(emailSubjectLine, builder, collaboratorNotification.UserEmail, collaboratorNotification.InvitedByEmail)); + } public string GetFrameworkUrl(int frameworkId, string tab) { var frameworkUrl = GetDLSUriBuilder(); frameworkUrl.Path += $"Framework/{frameworkId}/{tab}/"; return frameworkUrl.Uri.ToString(); } + public string GetCompetencyAssessmentUrl(int selfAssessmentID, string actionName) + { + var competencyAssessmentUrl = GetDLSUriBuilder(); + competencyAssessmentUrl.Path += $"CompetencyAssessments/{selfAssessmentID}/{actionName}"; + return competencyAssessmentUrl.Uri.ToString(); + } public string GetCurrentActivitiesUrl() { var dlsUrlBuilder = GetDLSUriBuilder(); @@ -204,7 +231,38 @@ public void SendReviewOutcomeNotification(int reviewId, int centreId) }; emailService.SendEmail(new Email(emailSubjectLine, builder, outcomeNotification.OwnerEmail, outcomeNotification.UserEmail)); } - + public void SendCompetencyAssessmentsReviewOutcomeNotification(int reviewId, int centreId) + { + string centreName = GetCentreName(centreId); + var outcomeNotification = competencyAssessmentService.GetSelfAssessmentReviewNotification(reviewId); + if (outcomeNotification == null) + { + throw new NotificationDataException($"No record found when trying to fetch review outcome Data. reviewId: {reviewId}"); + } + var competencyAssessmentUrl = GetCompetencyAssessmentUrl(outcomeNotification.SelfAssessmentID, "PublishReview"); + string emailSubjectLine = $"Competency Assessment Review Outcome - {(outcomeNotification.SignedOff ? "Approved" : "Rejected")} - Digital Learning Solutions"; + string approvalStatus = outcomeNotification.ReviewerFirstName + (outcomeNotification.SignedOff ? " approved the competency assessment for publishing." : " did not approve the competency assessment for publishing."); + string commentsText = outcomeNotification.ReviewerFirstName + (outcomeNotification.Comment != null ? " left the following review comment: " + outcomeNotification.Comment : " did not leave a review comment."); + string commentsHtml = "

" + outcomeNotification.ReviewerFirstName + (outcomeNotification.Comment != null ? " left the following review comment:


" + outcomeNotification.Comment + "


" : " did not leave a review comment.

"); + string reviewerFullName = $"{outcomeNotification.ReviewerFirstName} {outcomeNotification.ReviewerLastName} {(outcomeNotification.ReviewerActive == true ? "" : " (inactive)")}"; + var builder = new BodyBuilder + { + TextBody = $@"Dear {outcomeNotification.OwnerFirstName}, + Your competency assessment, {outcomeNotification.SelfAssessmentName}, has been reviewed by {reviewerFullName} ({outcomeNotification.UserEmail}) ({centreName}). + {approvalStatus} + {commentsText} + The full competency assessment review status, can be viewed by visiting: {competencyAssessmentUrl}. Once all of the required reviewers have approved the competency assessment, you may publish it. You will need to login to the Digital Learning Solutions platform to access the competency assessment.", + HtmlBody = $@" +

Dear {outcomeNotification.OwnerFirstName},

+

Your competency assessment, {outcomeNotification.SelfAssessmentName}, has been reviewed by {reviewerFullName} ({centreName}).

+

{approvalStatus}

+ {commentsHtml} +

Click here to view the full review status for the competency assessment. Once all of the required reviewers have approved the competency assessment, you may publish it.

+

You will need to login to the Digital Learning Solutions platform to access the competency assessment.

+ ", + }; + emailService.SendEmail(new Email(emailSubjectLine, builder, outcomeNotification.OwnerEmail, outcomeNotification.UserEmail)); + } public void SendSupervisorDelegateInvite(int supervisorDelegateId, int adminId, int centreId) { @@ -221,8 +279,8 @@ public void SendSupervisorDelegateInvite(int supervisorDelegateId, int adminId, builder.TextBody = $@"Dear colleague, You have been invited to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({supervisorDelegate.SupervisorEmail}) ({centreName}). To register, visit {dlsUrlBuilder.Uri.ToString()}. - Registering using this link will confirm your acceptance of the invite. Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results."; - builder.HtmlBody = $@"

Dear colleague,

You have been invited to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).

Click here to register and confirm your acceptance of the invite.

Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results.

"; + Registering using this link will confirm your acceptance of the invite. Your supervisor will then be able to assign competency assessments and view and validate your self assessment results."; + builder.HtmlBody = $@"

Dear colleague,

You have been invited to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).

Click here to register and confirm your acceptance of the invite.

Your supervisor will then be able to assign competency assessments and view and validate your self assessment results.

"; emailService.SendEmail(new Email(emailSubjectLine, builder, supervisorDelegate.DelegateEmail)); } } @@ -240,7 +298,7 @@ public void SendSupervisorDelegateConfirmed(int supervisorDelegateId, int adminI You are already registered as a delegate at the supervisor's DLS centre so they can now assign competency self assessments and view and validate your self assessment results. If this looks like a mistake, please contact {supervisorDelegate.SupervisorName} ({supervisorDelegate.SupervisorEmail}) ({centreName}) directly to correct."; - builder.HtmlBody = $@"

Dear {supervisorDelegate.FirstName}

{supervisorDelegate.SupervisorName} ({centreName}) has accepted your request to be your supervisor for profile asessment activities in the NHS England, Digital Learning Solutions platform.

Click here to access your role profile assessments.

"; + builder.HtmlBody = $@"

Dear {supervisorDelegate.FirstName}

{supervisorDelegate.SupervisorName} ({centreName}) has accepted your request to be your supervisor for profile asessment activities in the NHS England, Digital Learning Solutions platform.

Click here to access your competency assessments.

"; string toEmail = (@adminId == 0 ? supervisorDelegate.DelegateEmail : supervisorDelegate.SupervisorEmail); emailService.SendEmail(new Email(emailSubjectLine, builder, toEmail)); } @@ -410,8 +468,8 @@ public void SendSupervisorDelegateReminder(int supervisorDelegateId, int adminId builder.TextBody = $@"Dear colleague, This is a reminder to to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({supervisorDelegate.SupervisorEmail}) ({centreName}). To register, visit {dlsUrlBuilder.Uri.ToString()}. - Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results."; - builder.HtmlBody = $@"

Dear colleague,

This is a reminder to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).

Click here to register.

Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results.

"; + Your supervisor will then be able to assign competency assessments and view and validate your self assessment results."; + builder.HtmlBody = $@"

Dear colleague,

This is a reminder to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).

Click here to register.

Your supervisor will then be able to assign competency assessments and view and validate your self assessment results.

"; emailService.SendEmail(new Email(emailSubjectLine, builder, supervisorDelegate.DelegateEmail)); } } diff --git a/DigitalLearningSolutions.Web/Services/FrameworkService.cs b/DigitalLearningSolutions.Web/Services/FrameworkService.cs index 6e2c1790e9..81a3cd1a8f 100644 --- a/DigitalLearningSolutions.Web/Services/FrameworkService.cs +++ b/DigitalLearningSolutions.Web/Services/FrameworkService.cs @@ -46,9 +46,9 @@ public interface IFrameworkService CollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId); // Competencies/groups: - IEnumerable GetFrameworkCompetencyGroups(int frameworkId); + IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId); - IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId); + IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId); CompetencyGroupBase? GetCompetencyGroupBaseById(int Id); @@ -262,6 +262,9 @@ bool deleteFromExisting void DeleteCompetencyLearningResource(int competencyLearningResourceId, int adminId); void UpdateFrameworkCompetencyFrameworkCompetencyGroup(int? competencyGroupId, int frameworkCompetencyGroupId, int adminId); + + void ChangeFrameworkOwner(int frameworkId, string newOwnerEmail, int updatedByAdminId); + IEnumerable GetAdminIdsForUserId(int userId); } public class FrameworkService : IFrameworkService { @@ -481,9 +484,9 @@ public IEnumerable GetFrameworkByFrameworkName(string framewor return frameworkDataService.GetFrameworkByFrameworkName(frameworkName, adminId); } - public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId) + public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId) { - return frameworkDataService.GetFrameworkCompetenciesUngrouped(frameworkId); + return frameworkDataService.GetFrameworkCompetenciesUngrouped(frameworkId, assessmentId); } public FrameworkCompetency? GetFrameworkCompetencyById(int Id) @@ -496,9 +499,9 @@ public IEnumerable GetFrameworkCompetenciesUngrouped(int fr return frameworkDataService.GetFrameworkCompetencyForPreview(frameworkCompetencyId); } - public IEnumerable GetFrameworkCompetencyGroups(int frameworkId) + public IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId) { - return frameworkDataService.GetFrameworkCompetencyGroups(frameworkId); + return frameworkDataService.GetFrameworkCompetencyGroups(frameworkId, assessmentId); } public string? GetFrameworkConfigForFrameworkId(int frameworkId) @@ -603,12 +606,12 @@ public int InsertComment(int frameworkId, int adminId, string comment, int? repl public int InsertCompetency(string name, string? description, int adminId, bool alwaysShowDescription = false) { - return frameworkDataService.InsertCompetency(name, description, adminId, alwaysShowDescription); + return frameworkDataService.InsertCompetency(name.Trim(), description, adminId, alwaysShowDescription); } public int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId, int? frameworkId) { - return frameworkDataService.InsertCompetencyGroup(groupName, groupDescription, adminId, frameworkId); + return frameworkDataService.InsertCompetencyGroup(groupName.Trim(), groupDescription, adminId, frameworkId); } public int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId, bool addDefaultQuestions = true) @@ -683,12 +686,12 @@ public int UpdateCompetencyFlags(int frameworkId, int competencyId, int[] select public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription) { - frameworkDataService.UpdateFrameworkCompetency(frameworkCompetencyId, name, description, adminId, alwaysShowDescription); + frameworkDataService.UpdateFrameworkCompetency(frameworkCompetencyId, name.Trim(), description, adminId, alwaysShowDescription); } public bool UpdateFrameworkCompetencyGroup(int frameworkCompetencyGroupId, int competencyGroupId, string name, string? description, int adminId) { - return frameworkDataService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupId, name, description, adminId); + return frameworkDataService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupId, name.Trim(), description, adminId); } public void UpdateFrameworkConfig(int frameworkId, int adminId, string? frameworkConfig) @@ -735,5 +738,14 @@ public void UpdateFrameworkCompetencyFrameworkCompetencyGroup(int? competencyGro { frameworkDataService.UpdateFrameworkCompetencyFrameworkCompetencyGroup(competencyGroupId, frameworkCompetencyGroupId, adminId); } + public void ChangeFrameworkOwner(int frameworkId, string newOwnerEmail, int updatedByAdminId) + { + frameworkDataService.ChangeFrameworkOwner(frameworkId, newOwnerEmail, updatedByAdminId); + } + + public IEnumerable GetAdminIdsForUserId(int userId) + { + return frameworkDataService.GetAdminIdsForUserId(userId); + } } } diff --git a/DigitalLearningSolutions.Web/Services/GroupsService.cs b/DigitalLearningSolutions.Web/Services/GroupsService.cs index 176bc15997..013a08963b 100644 --- a/DigitalLearningSolutions.Web/Services/GroupsService.cs +++ b/DigitalLearningSolutions.Web/Services/GroupsService.cs @@ -721,7 +721,11 @@ IEnumerable delegateNotificationPreferences delegateUserId, groupCourse.CustomisationId ); - + // If the delegate has already completed this course, do nothing + if (candidateProgressOnCourse.Any(p => p.Completed != null)) + { + return; + } var existingRecordsToUpdate = candidateProgressOnCourse.Where( p => ProgressShouldBeUpdatedOnEnrolment(p, isAddCourseToGroup) ).ToList(); diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index 0b4b100db4..a1987466e2 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -37,7 +37,7 @@ public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook var newCompetencyIds = competencyRows.Select(row => row.ID ?? 0).ToList(); var existingIds = frameworkService.GetFrameworkCompetencyOrder(frameworkId, newCompetencyIds); var existingGroups = frameworkService - .GetFrameworkCompetencyGroups(frameworkId).Where(x => x.FrameworkCompetencies.Any()).ToList() + .GetFrameworkCompetencyGroups(frameworkId, null).Where(x => x.FrameworkCompetencies.Any()).ToList() .Select(row => row.Name) .Distinct() .ToList(); @@ -152,7 +152,7 @@ internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int a .ToList(); for (int i = 0; i < competencyGroupCount; i++) { - var existingGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).Select(row => new { row.ID, row.Name }) + var existingGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId, null).Select(row => new { row.ID, row.Name }) .Distinct() .ToList(); var placesToMove = Math.Abs(existingGroups.FindIndex(group => group.Name == distinctCompetencyGroups[i]) - i); @@ -208,7 +208,6 @@ CompetencyTableRow competencyRow else { frameworkCompetencyGroupId = frameworkService.GetFrameworkCompetencyGroupId(frameworkId, newCompetencyGroupId); - var isUpdated = frameworkService.UpdateFrameworkCompetencyGroup((int)frameworkCompetencyGroupId, newCompetencyGroupId, competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminId); competencyRow.RowStatus = RowStatus.CompetencyGroupUpdated; } diff --git a/DigitalLearningSolutions.Web/Services/NotificationService.cs b/DigitalLearningSolutions.Web/Services/NotificationService.cs index cd776d090e..066b6c12be 100644 --- a/DigitalLearningSolutions.Web/Services/NotificationService.cs +++ b/DigitalLearningSolutions.Web/Services/NotificationService.cs @@ -159,7 +159,11 @@ int numLearningLogItemsAffected " Note: This message has been copied to the administrator(s) managing this activity, for their information."; } - const string emailSubjectLine = "Digital Learning Solutions Activity Complete"; + string courseName = progressCompletionData.CourseName.Length > 30 + ? progressCompletionData.CourseName.Substring(0, 30) + : progressCompletionData.CourseName; + + string emailSubjectLine = $"Digital Learning Solutions Activity Complete - {courseName}"; var delegateNameOrGenericTitle = progress.DelegateFirstName ?? "Digital Learning Solutions Delegate"; var emailsToCc = GetEmailsToCc( progressCompletionData.AdminId, diff --git a/DigitalLearningSolutions.Web/Services/RoleProfileService.cs b/DigitalLearningSolutions.Web/Services/RoleProfileService.cs deleted file mode 100644 index 7983b2afa4..0000000000 --- a/DigitalLearningSolutions.Web/Services/RoleProfileService.cs +++ /dev/null @@ -1,68 +0,0 @@ -using DigitalLearningSolutions.Data.DataServices; -using DigitalLearningSolutions.Data.Models.RoleProfiles; -using System.Collections.Generic; - -namespace DigitalLearningSolutions.Web.Services -{ - public interface IRoleProfileService - { - //GET DATA - IEnumerable GetAllRoleProfiles(int adminId); - - IEnumerable GetRoleProfilesForAdminId(int adminId); - - RoleProfileBase? GetRoleProfileBaseById(int roleProfileId, int adminId); - - RoleProfileBase? GetRoleProfileByName(string roleProfileName, int adminId); - - IEnumerable GetNRPProfessionalGroups(); - - //UPDATE DATA - bool UpdateRoleProfileName(int roleProfileId, int adminId, string roleProfileName); - - bool UpdateRoleProfileProfessionalGroup(int roleProfileId, int adminId, int? nrpProfessionalGroupID); - - } - public class RoleProfileService : IRoleProfileService - { - private readonly IRoleProfileDataService roleProfileDataService; - public RoleProfileService(IRoleProfileDataService roleProfileDataService) - { - this.roleProfileDataService = roleProfileDataService; - } - public IEnumerable GetAllRoleProfiles(int adminId) - { - return roleProfileDataService.GetAllRoleProfiles(adminId); - } - - public IEnumerable GetNRPProfessionalGroups() - { - return roleProfileDataService.GetNRPProfessionalGroups(); - } - - public RoleProfileBase? GetRoleProfileBaseById(int roleProfileId, int adminId) - { - return roleProfileDataService.GetRoleProfileBaseById(roleProfileId, adminId); - } - - public RoleProfileBase? GetRoleProfileByName(string roleProfileName, int adminId) - { - return roleProfileDataService.GetRoleProfileByName(roleProfileName, adminId); - } - - public IEnumerable GetRoleProfilesForAdminId(int adminId) - { - return roleProfileDataService.GetRoleProfilesForAdminId(adminId); - } - - public bool UpdateRoleProfileName(int roleProfileId, int adminId, string roleProfileName) - { - return roleProfileDataService.UpdateRoleProfileName(roleProfileId, adminId, roleProfileName); - } - - public bool UpdateRoleProfileProfessionalGroup(int roleProfileId, int adminId, int? nrpProfessionalGroupID) - { - return roleProfileDataService.UpdateRoleProfileProfessionalGroup(roleProfileId, adminId, nrpProfessionalGroupID); - } - } -} diff --git a/DigitalLearningSolutions.Web/Services/SelfAssessmentNotificationService.cs b/DigitalLearningSolutions.Web/Services/SelfAssessmentNotificationService.cs new file mode 100644 index 0000000000..caa3910c70 --- /dev/null +++ b/DigitalLearningSolutions.Web/Services/SelfAssessmentNotificationService.cs @@ -0,0 +1,70 @@ +namespace DigitalLearningSolutions.Web.Services +{ + using System; + using DigitalLearningSolutions.Data.Constants; + using DigitalLearningSolutions.Data.DataServices; + using DigitalLearningSolutions.Data.Models; + using DigitalLearningSolutions.Data.Models.Email; + using MimeKit; + + public interface ISelfAssessmentNotificationService + { + void SendCompetencyAssessmentCollaboratorInvite(int id, int invitedByAdminId); + } + + public class SelfAssessmentNotificationService : ISelfAssessmentNotificationService + { + + private readonly IConfigDataService configDataService; + private readonly IEmailService emailService; + private readonly ICompetencyAssessmentService competencyAssessmentService; + public SelfAssessmentNotificationService( + ICompetencyAssessmentService competencyAssessmentService, + IConfigDataService configDataService, + IEmailService emailService + ) + { + this.competencyAssessmentService = competencyAssessmentService; + this.configDataService = configDataService; + this.emailService = emailService; + } + public void SendCompetencyAssessmentCollaboratorInvite(int id, int invitedByAdminId) + { + var collaboratorNotification = competencyAssessmentService.GetCollaboratorNotification(id, invitedByAdminId); + if (collaboratorNotification == null) + { + throw new NotificationDataException($"No record found when trying to fetch collaboratorNotification Data. id: {id}, invitedByAdminId: {invitedByAdminId})"); + } + + var competencyAssessmentUrl = GetCompetencyAssessmentkUrl(collaboratorNotification.SelfAssessmentID, "Manage"); + string emailSubjectLine = $"Competency assessment {collaboratorNotification.CompetencyAssessmentRole} Invitation - Digital Learning Solutions"; + + var builder = new BodyBuilder + { + TextBody = $@"Dear colleague, + You have been identified as a {collaboratorNotification.CompetencyAssessmentRole} for the competency assessment, {collaboratorNotification.CompetencyAssessmentName}, by {collaboratorNotification.InvitedByName} ({collaboratorNotification.InvitedByEmail}). + To access the competency assessment, visit this url: {competencyAssessmentUrl}. You will need to be registered on the Digital Learning Solutions platform to view the competency assessment.", + HtmlBody = $@" +

Dear colleague,

+

You have been identified as a {collaboratorNotification.CompetencyAssessmentRole} for the competency assessment, {collaboratorNotification.CompetencyAssessmentName}, by {collaboratorNotification.InvitedByName}.

+

Click here to access the competency assessment. You will need to be registered on the Digital Learning Solutions platform to view the competency assessment.

+ ", + }; + emailService.SendEmail(new Email(emailSubjectLine, builder, collaboratorNotification.UserEmail, collaboratorNotification.InvitedByEmail)); + } + + public string GetCompetencyAssessmentkUrl(int competencyAssessmentID, string tab) + { + var competencyAssessmentUrl = GetDLSUriBuilder(); + competencyAssessmentUrl.Path += $"CompetencyAssessments/{competencyAssessmentID}/{tab}/"; + return competencyAssessmentUrl.Uri.ToString(); + } + public UriBuilder GetDLSUriBuilder() + { + var trackingSystemBaseUrl = configDataService.GetConfigValue(ConfigConstants.AppBaseUrl) ?? + throw new ConfigValueMissingException(configDataService.GetConfigValueMissingExceptionMessage("AppBaseUrl")); + return new UriBuilder(trackingSystemBaseUrl); + } + + } +} diff --git a/DigitalLearningSolutions.Web/Services/SupervisorService.cs b/DigitalLearningSolutions.Web/Services/SupervisorService.cs index 8cd8d2e16e..9861caa0ed 100644 --- a/DigitalLearningSolutions.Web/Services/SupervisorService.cs +++ b/DigitalLearningSolutions.Web/Services/SupervisorService.cs @@ -1,5 +1,5 @@ using DigitalLearningSolutions.Data.DataServices; -using DigitalLearningSolutions.Data.Models.RoleProfiles; +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; using DigitalLearningSolutions.Data.Models.SelfAssessments; using DigitalLearningSolutions.Data.Models.Supervisor; using System; @@ -20,8 +20,8 @@ public interface ISupervisorService IEnumerable GetSupervisorDashboardToDoItemsForRequestedSignOffs(int adminId); IEnumerable GetSupervisorDashboardToDoItemsForRequestedReviews(int adminId); DelegateSelfAssessment? GetSelfAssessmentBaseByCandidateAssessmentId(int candidateAssessmentId, int? adminIdCategoryId); - IEnumerable GetAvailableRoleProfilesForDelegate(int candidateId, int centreId, int? categoryId); - RoleProfile? GetRoleProfileById(int selfAssessmentId); + IEnumerable GetAvailableCompetencyAssessmentsForDelegate(int candidateId, int centreId, int? categoryId); + CompetencyAssessment? GetCompetencyAssessmentById(int selfAssessmentId); IEnumerable GetSupervisorRolesForSelfAssessment(int selfAssessmentId); IEnumerable GetSupervisorRolesBySelfAssessmentIdForSupervisor(int selfAssessmentId); IEnumerable GetDelegateNominatableSupervisorRolesForSelfAssessment(int selfAssessmentId); @@ -37,7 +37,6 @@ public interface ISupervisorService SupervisorDelegateDetail GetSupervisorDelegateDetailsByIdWithoutRemoveClause(int supervisorDelegateId, int adminId, int delegateUserId); //UPDATE DATA - bool ConfirmSupervisorDelegateById(int supervisorDelegateId, int candidateId, int adminId); bool RemoveSupervisorDelegateById(int supervisorDelegateId, int delegateUserId, int adminId); bool UpdateSelfAssessmentResultSupervisorVerifications(int selfAssessmentResultSupervisorVerificationId, string? comments, bool signedOff, int adminId); bool UpdateSelfAssessmentResultSupervisorVerificationsEmailSent(int selfAssessmentResultSupervisorVerificationId); @@ -70,19 +69,14 @@ public int AddSuperviseDelegate(int? supervisorAdminId, int? delegateUserId, str return supervisorDataService.AddSuperviseDelegate(supervisorAdminId, delegateUserId, delegateEmail, supervisorEmail, centreId); } - public bool ConfirmSupervisorDelegateById(int supervisorDelegateId, int candidateId, int adminId) - { - return supervisorDataService.ConfirmSupervisorDelegateById(supervisorDelegateId, candidateId, adminId); - } - public int EnrolDelegateOnAssessment(int delegateUserId, int supervisorDelegateId, int selfAssessmentId, DateTime? completeByDate, int? selfAssessmentSupervisorRoleId, int adminId, int centreId, bool isLoggedInUser) { return supervisorDataService.EnrolDelegateOnAssessment(delegateUserId, supervisorDelegateId, selfAssessmentId, completeByDate, selfAssessmentSupervisorRoleId, adminId, centreId, isLoggedInUser); } - public IEnumerable GetAvailableRoleProfilesForDelegate(int candidateId, int centreId, int? categoryId) + public IEnumerable GetAvailableCompetencyAssessmentsForDelegate(int candidateId, int centreId, int? categoryId) { - return supervisorDataService.GetAvailableRoleProfilesForDelegate(candidateId, centreId, categoryId); + return supervisorDataService.GetAvailableCompetencyAssessmentsForDelegate(candidateId, centreId, categoryId); } public CandidateAssessmentSupervisor? GetCandidateAssessmentSupervisor(int candidateAssessmentID, int supervisorDelegateId, int selfAssessmentSupervisorRoleId) @@ -110,9 +104,9 @@ public IEnumerable GetDelegateNominatableSuperviso return supervisorDataService.GetDelegateNominatableSupervisorRolesForSelfAssessment(selfAssessmentId); } - public RoleProfile? GetRoleProfileById(int selfAssessmentId) + public CompetencyAssessment? GetCompetencyAssessmentById(int selfAssessmentId) { - return supervisorDataService.GetRoleProfileById(selfAssessmentId); + return supervisorDataService.GetCompetencyAssessmentById(selfAssessmentId); } public DelegateSelfAssessment? GetSelfAssessmentBaseByCandidateAssessmentId(int candidateAssessmentId, int? adminIdCategoryId) diff --git a/DigitalLearningSolutions.Web/Startup.cs b/DigitalLearningSolutions.Web/Startup.cs index 53451b05bd..bf2fba98da 100644 --- a/DigitalLearningSolutions.Web/Startup.cs +++ b/DigitalLearningSolutions.Web/Startup.cs @@ -435,7 +435,7 @@ private static void RegisterServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -469,6 +469,7 @@ private static void RegisterServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } private static void RegisterDataServices(IServiceCollection services) @@ -508,7 +509,7 @@ private static void RegisterDataServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -618,7 +619,6 @@ public void Configure(IApplicationBuilder app, IMigrationRunner migrationRunner, if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); - app.UseBrowserLink(); } diff --git a/DigitalLearningSolutions.Web/Styles/jodit.scss b/DigitalLearningSolutions.Web/Styles/jodit.scss index 076a7f0053..416099e8f7 100644 --- a/DigitalLearningSolutions.Web/Styles/jodit.scss +++ b/DigitalLearningSolutions.Web/Styles/jodit.scss @@ -4,3 +4,6 @@ border: 2px solid red !important; border-radius: 4px; } +.jodit-placeholder { + display: none !important; +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesSelectFrameworkFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesSelectFrameworkFormData.cs new file mode 100644 index 0000000000..1dc5b60ed9 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesSelectFrameworkFormData.cs @@ -0,0 +1,10 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using System.ComponentModel.DataAnnotations; + public class AddCompetenciesSelectFrameworkFormData + { + public int ID { get; set; } + [Required(ErrorMessage = "Select a linked framework")] + public int? FrameworkId { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesSelectFrameworkViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesSelectFrameworkViewModel.cs new file mode 100644 index 0000000000..98a09de671 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesSelectFrameworkViewModel.cs @@ -0,0 +1,28 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Data.Models.Frameworks; + using DigitalLearningSolutions.Web.Helpers; + using System.Collections.Generic; + + public class AddCompetenciesSelectFrameworkViewModel : AddCompetenciesSelectFrameworkFormData + { + public AddCompetenciesSelectFrameworkViewModel( + CompetencyAssessmentBase competencyAssessmentBase, + IEnumerable linkedFrameworks + ) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + LinkedFrameworks = linkedFrameworks; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + } + public string? CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public IEnumerable LinkedFrameworks { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesViewFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesViewFormData.cs new file mode 100644 index 0000000000..0e6d2ed887 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesViewFormData.cs @@ -0,0 +1,9 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class AddCompetenciesFormData + { + public int ID { get; set; } + public int[] SelectedCompetencyIds { get; set; } + public int FrameworkId { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesViewModel.cs new file mode 100644 index 0000000000..38b9e87765 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AddCompetenciesViewModel.cs @@ -0,0 +1,33 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Data.Models.Frameworks; + using DigitalLearningSolutions.Web.Helpers; + using System.Collections.Generic; + public class AddCompetenciesViewModel + { + public AddCompetenciesViewModel(CompetencyAssessmentBase competencyAssessmentBase, IEnumerable groupedCompetencies, IEnumerable ungroupedCompetencies, int frameworkId, string? frameworkName, int[] selectedFrameworkCompetencies) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + GroupedCompetencies = groupedCompetencies; + UngroupedCompetencies = ungroupedCompetencies; + FrameworkId = frameworkId; + FrameworkName = frameworkName; + SelectedCompetencyIds = selectedFrameworkCompetencies; + } + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public IEnumerable GroupedCompetencies { get; set; } + public IEnumerable UngroupedCompetencies { get; set; } + public int[] SelectedCompetencyIds { get; set; } + public int FrameworkId { get; set; } + public string? FrameworkName { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AllCompetencyAssessmentsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AllCompetencyAssessmentsViewModel.cs new file mode 100644 index 0000000000..a858706127 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/AllCompetencyAssessmentsViewModel.cs @@ -0,0 +1,42 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using System.Collections.Generic; + using System.Linq; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Web.Helpers; + using Microsoft.AspNetCore.Mvc.Rendering; + public class AllCompetencyAssessmentsViewModel : BaseCompetencyAssessmentsPageViewModel + { + public readonly IEnumerable CompetencyAssessments; + public readonly bool IsWorkforceManager; + public override SelectList CompetencyAssessmentSortByOptions { get; } = new SelectList(new[] + { + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentName, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentOwner, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentCreatedDate, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentPublishStatus, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentBrand, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentNationalRoleGroup, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentNationalCompetencyAssessment + }); + public AllCompetencyAssessmentsViewModel( + IEnumerable competencyAssessments, + string? searchString, + string sortBy, + string sortDirection, + int page + ) : base(searchString, sortBy, sortDirection, page) + { + var sortedItems = SortingHelper.SortCompetencyAssessmentItems( + competencyAssessments, + sortBy, + sortDirection + ); + var filteredItems = SearchHelper.FilterCompetencyAssessments(sortedItems, SearchString, 60, false).ToList(); + MatchingSearchResults = filteredItems.Count; + SetTotalPages(); + CompetencyAssessments = PaginateItems(filteredItems).Cast(); + } + } +} + diff --git a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/BaseRoleProfileViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/BaseCompetencyAssessmentsPageViewModel.cs similarity index 61% rename from DigitalLearningSolutions.Web/ViewModels/RoleProfiles/BaseRoleProfileViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/BaseCompetencyAssessmentsPageViewModel.cs index 0b5fa8d753..01a65ba1ca 100644 --- a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/BaseRoleProfileViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/BaseCompetencyAssessmentsPageViewModel.cs @@ -1,71 +1,71 @@ -namespace DigitalLearningSolutions.Web.ViewModels.RoleProfiles -{ - using System; - using System.Collections.Generic; - using System.Linq; - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.Rendering; - - public abstract class BaseRoleProfilesPageViewModel - { - [BindProperty] public string SortDirection { get; set; } - [BindProperty] public string SortBy { get; set; } - public int Page { get; protected set; } - public int TotalPages { get; protected set; } - public int MatchingSearchResults; - public abstract SelectList RoleProfileSortByOptions { get; } - - public const string DescendingText = "Descending"; - public const string AscendingText = "Ascending"; - - private const int ItemsPerPage = 12; - - public readonly string? SearchString; - - protected BaseRoleProfilesPageViewModel( - string? searchString, - string sortBy, - string sortDirection, - int page - ) - { - SortBy = sortBy; - SortDirection = sortDirection; - SearchString = searchString; - Page = page; - } - protected IEnumerable PaginateItems(IList items) - { - if (items.Count > ItemsPerPage) - { - items = items.Skip(OffsetFromPageNumber(Page)).Take(ItemsPerPage).ToList(); - } - - return items; - } - protected void SetTotalPages() - { - TotalPages = (int)Math.Ceiling(MatchingSearchResults / (double)ItemsPerPage); - if (Page < 1 || Page > TotalPages) - { - Page = 1; - } - } - - private int OffsetFromPageNumber(int pageNumber) => - (pageNumber - 1) * ItemsPerPage; - } - - public static class RoleProfileSortByOptionTexts - { - public const string - RoleProfileName = "Profile Name", - RoleProfileOwner = "Owner", - RoleProfileCreatedDate = "Created Date", - RoleProfilePublishStatus = "Publish Status", - RoleProfileBrand = "Brand", - RoleProfileNationalRoleGroup = "National Job Group", - RoleProfileNationalRoleProfile = "National Job Profile"; - } -} +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using System; + using System.Collections.Generic; + using System.Linq; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.Rendering; + + public abstract class BaseCompetencyAssessmentsPageViewModel + { + [BindProperty] public string SortDirection { get; set; } + [BindProperty] public string SortBy { get; set; } + public int Page { get; protected set; } + public int TotalPages { get; protected set; } + public int MatchingSearchResults; + public abstract SelectList CompetencyAssessmentSortByOptions { get; } + + public const string DescendingText = "Descending"; + public const string AscendingText = "Ascending"; + + private const int ItemsPerPage = 12; + + public readonly string? SearchString; + + protected BaseCompetencyAssessmentsPageViewModel( + string? searchString, + string sortBy, + string sortDirection, + int page + ) + { + SortBy = sortBy; + SortDirection = sortDirection; + SearchString = searchString; + Page = page; + } + protected IEnumerable PaginateItems(IList items) + { + if (items.Count > ItemsPerPage) + { + items = items.Skip(OffsetFromPageNumber(Page)).Take(ItemsPerPage).ToList(); + } + + return items; + } + protected void SetTotalPages() + { + TotalPages = (int)Math.Ceiling(MatchingSearchResults / (double)ItemsPerPage); + if (Page < 1 || Page > TotalPages) + { + Page = 1; + } + } + + private int OffsetFromPageNumber(int pageNumber) => + (pageNumber - 1) * ItemsPerPage; + } + + public static class CompetencyAssessmentSortByOptionTexts + { + public const string + CompetencyAssessmentName = "Competency Assessment", + CompetencyAssessmentOwner = "Owner", + CompetencyAssessmentCreatedDate = "Created Date", + CompetencyAssessmentPublishStatus = "Publish Status", + CompetencyAssessmentBrand = "Brand", + CompetencyAssessmentNationalRoleGroup = "National Job Group", + CompetencyAssessmentNationalCompetencyAssessment = "National Job Profile"; + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/BaseSignoffDeclarationViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/BaseSignoffDeclarationViewModel.cs new file mode 100644 index 0000000000..d19afb2c30 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/BaseSignoffDeclarationViewModel.cs @@ -0,0 +1,29 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class BaseSignoffDeclarationViewModel + { + protected BaseSignoffDeclarationViewModel() { } + protected BaseSignoffDeclarationViewModel(int id) + { + CompetencyAssessmentId = id; + } + + public int CompetencyAssessmentId { get; set; } + public string? CompetencyAssessmentName { get; set; } + public int? DeclarationValue { get; set; } + public string? ActionName { get; set; } + public string? CustomText { get; set; } + public string? DefaultText { get; set; } + public string? Declaration => DeclarationValue == 1 ? "Custom" : "Default"; + public string? DeclarationText => DeclarationValue == 1 ? CustomText : DefaultText; + protected void CopyProperties(BaseSignoffDeclarationViewModel model) + { + CompetencyAssessmentId = model.CompetencyAssessmentId; + CompetencyAssessmentName = model.CompetencyAssessmentName; + CustomText = model.CustomText; + DefaultText = model.DefaultText.Replace("{{CompetencyAssessmentName}}", model.CompetencyAssessmentName); + DeclarationValue = model.DeclarationValue; + ActionName = model.ActionName; + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentFeaturesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentFeaturesViewModel.cs new file mode 100644 index 0000000000..a7eb36d121 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentFeaturesViewModel.cs @@ -0,0 +1,57 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using System.Collections.Generic; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class CompetencyAssessmentFeaturesViewModel + { + public CompetencyAssessmentFeaturesViewModel() + { } + public CompetencyAssessmentFeaturesViewModel(int id, string competencyAssessmentName, int userRole, int? frameworkId) + { + ID = id; + CompetencyAssessmentName = competencyAssessmentName; + UserRole = userRole; + FrameworkId = frameworkId; + DescriptionStatus = true; + ProviderandCategoryStatus = true; + VocabularyStatus = true; + WorkingGroupStatus = true; + + } + + public CompetencyAssessmentFeaturesViewModel(CompetencyAssessmentFeaturesViewModel features) + { + ID = features.ID; + CompetencyAssessmentName = features.CompetencyAssessmentName; + DescriptionStatus = features.DescriptionStatus; + ProviderandCategoryStatus = features.ProviderandCategoryStatus; + VocabularyStatus = features.VocabularyStatus; + WorkingGroupStatus = features.WorkingGroupStatus; + AllframeworkCompetenciesStatus = features.AllframeworkCompetenciesStatus; + FrameworkId = features.FrameworkId.Value; + } + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + public int UserRole { get; set; } + public bool DescriptionStatus { get; set; } + public bool ProviderandCategoryStatus { get; set; } + public bool VocabularyStatus { get; set; } + public bool WorkingGroupStatus { get; set; } + public bool AllframeworkCompetenciesStatus { get; set; } + public int? FrameworkId { get; set; } + public IEnumerable SelectedFeatures + { + get + { + var features = new List(); + if (DescriptionStatus) features.Add("Description"); + if (ProviderandCategoryStatus) features.Add("Provider and category"); + if (VocabularyStatus) features.Add("Vocabulary"); + if (WorkingGroupStatus) features.Add("Working group"); + if (AllframeworkCompetenciesStatus) features.Add("All framework competencies"); + return features; + } + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentPreviewViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentPreviewViewModel.cs new file mode 100644 index 0000000000..481517f039 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentPreviewViewModel.cs @@ -0,0 +1,17 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class CompetencyAssessmentPreviewViewModel + { + public CompetencyAssessmentPreviewViewModel() + { } + public CompetencyAssessmentPreviewViewModel(int competencyAssessmentId, int centreId, string centreName) + { + CompetencyAssessmentId = competencyAssessmentId; + CentreId = centreId; + CentreName = centreName; + } + public int CompetencyAssessmentId { get; set; } + public int CentreId { get; set; } + public string CentreName { get; set; } = string.Empty; + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentsViewModel.cs new file mode 100644 index 0000000000..f091a7dd76 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyAssessmentsViewModel.cs @@ -0,0 +1,29 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Web.Models.Enums; + using DigitalLearningSolutions.Web.ViewModels.Common; + + public class CompetencyAssessmentsViewModel + { + public CompetencyAssessmentsViewModel( + bool isWorkforceManager, + bool isWorkforceContributor, + AllCompetencyAssessmentsViewModel allCompetencyAssessments, + MyCompetencyAssessmentsViewModel myCompetencyAssessments, + CompetencyAssessmentsTab currentTab + ) + { + IsWorkforceManager = isWorkforceManager; + IsWorkforceContributor = isWorkforceContributor; + MyCompetencyAssessmentsViewModel = myCompetencyAssessments; + AllCompetencyAssessmentsViewModel = allCompetencyAssessments; + TabNavLinks = new TabsNavViewModel(currentTab); + } + + public bool IsWorkforceManager { get; set; } + public bool IsWorkforceContributor { get; set; } + public MyCompetencyAssessmentsViewModel MyCompetencyAssessmentsViewModel { get; set; } + public AllCompetencyAssessmentsViewModel AllCompetencyAssessmentsViewModel { get; set; } + public TabsNavViewModel TabNavLinks { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyGroupDeleteViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyGroupDeleteViewModel.cs new file mode 100644 index 0000000000..ee7819c95e --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/CompetencyGroupDeleteViewModel.cs @@ -0,0 +1,23 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class CompetencyGroupDeleteViewModel + { + public CompetencyGroupDeleteViewModel(int competencyAssessmentId, int competencyGroupId, int competencyCount, string? Vocabulary) + { + CompetencyAssessmentId = competencyAssessmentId; + CompetencyGroupId = competencyGroupId; + CompetencyCount = competencyCount; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(Vocabulary); + } + public int CompetencyAssessmentId { get; set; } + public int CompetencyGroupId { get; set; } + public int CompetencyCount { get; set; } + public string? Vocabulary { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmChangePrimaryFrameworkViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmChangePrimaryFrameworkViewModel.cs new file mode 100644 index 0000000000..9c24ba5640 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmChangePrimaryFrameworkViewModel.cs @@ -0,0 +1,28 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Data.Models.Frameworks; +using DigitalLearningSolutions.Web.Attributes; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ConfirmChangePrimaryFrameworkViewModel + { + public ConfirmChangePrimaryFrameworkViewModel() + {} + public ConfirmChangePrimaryFrameworkViewModel(CompetencyAssessmentBase competencyAssessmentBase, DetailFramework framework) + { + CompetencyAssessmentId = competencyAssessmentBase.ID; + AssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + FrameworkName = framework.FrameworkName; + FrameworkId = framework.ID; + Vocabulary = competencyAssessmentBase.Vocabulary; + } + public int CompetencyAssessmentId { get; set; } + public int UserRole { get; set; } + public string? AssessmentName { get; set; } + public string? FrameworkName { get; set; } + public int FrameworkId { get; set; } + public string? Vocabulary { get; set; } + [BooleanMustBeTrue(ErrorMessage = "You must confirm that you wish to change the primary framework")] + public bool Confirm { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmMakePrimaryFrameworkViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmMakePrimaryFrameworkViewModel.cs new file mode 100644 index 0000000000..bc069c865a --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmMakePrimaryFrameworkViewModel.cs @@ -0,0 +1,33 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Data.Models.Frameworks; +using System.ComponentModel.DataAnnotations; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ConfirmMakePrimaryFrameworkViewModel + { + public ConfirmMakePrimaryFrameworkViewModel() + {} + public ConfirmMakePrimaryFrameworkViewModel(CompetencyAssessmentBase competencyAssessmentBase, DetailFramework framework) + { + CompetencyAssessmentId = competencyAssessmentBase.ID; + AssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + FrameworkName = framework.FrameworkName; + FrameworkId = framework.ID; + Vocabulary = competencyAssessmentBase.Vocabulary; + } + public int CompetencyAssessmentId { get; set; } + public int UserRole { get; set; } + public string? AssessmentName { get; set; } + public string? FrameworkName { get; set; } + public int FrameworkId { get; set; } + public string? Vocabulary { get; set; } + [Required(ErrorMessage = "You need to confirm that you want to make this the primary framework")] + public bool? Confirm { get; set; } + public bool DescriptionStatus { get; set; } + public bool ProviderandCategoryStatus { get; set; } + public bool VocabularyStatus { get; set; } + public bool WorkingGroupStatus { get; set; } + public bool AllframeworkCompetenciesStatus { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmRemoveFrameworkSourceViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmRemoveFrameworkSourceViewModel.cs new file mode 100644 index 0000000000..a6fdafc9f3 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ConfirmRemoveFrameworkSourceViewModel.cs @@ -0,0 +1,29 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Data.Models.Frameworks; +using DigitalLearningSolutions.Web.Attributes; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ConfirmRemoveFrameworkSourceViewModel + { + public ConfirmRemoveFrameworkSourceViewModel() { } + public ConfirmRemoveFrameworkSourceViewModel(CompetencyAssessmentBase competencyAssessmentBase, DetailFramework framework, int competencyCount) + { + CompetencyAssessmentId = competencyAssessmentBase.ID; + CompetencyCount = competencyCount; + AssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + FrameworkName = framework.FrameworkName; + FrameworkId = framework.ID; + Vocabulary = competencyAssessmentBase.Vocabulary ?? "proficiencies"; + } + public int CompetencyAssessmentId { get; set; } + public string? AssessmentName { get; set; } + public string? FrameworkName { get; set; } + public int FrameworkId { get; set; } + public int CompetencyCount { get; set; } + public string? Vocabulary { get; set; } + [BooleanMustBeTrue(ErrorMessage = "You must confirm that you wish to remove this framework")] + public bool Confirm { get; set; } + + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditBrandingViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditBrandingViewModel.cs new file mode 100644 index 0000000000..b9b671c7d1 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditBrandingViewModel.cs @@ -0,0 +1,33 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class EditBrandingViewModel + { + public EditBrandingViewModel() { } + public EditBrandingViewModel(CompetencyAssessment competencyAssessment, SelectList brandSelectList, SelectList categorySelectList, bool? taskStatus) + { + ID = competencyAssessment.ID; + CompetencyAssessmentName = competencyAssessment.CompetencyAssessmentName; + BrandID = competencyAssessment.BrandID; + CategoryID = competencyAssessment.CategoryID; + Brand = competencyAssessment.Brand; + Category = competencyAssessment.Category; + UserRole = competencyAssessment.UserRole; + BrandSelectList = brandSelectList; + CategorySelectList = categorySelectList; + TaskStatus = taskStatus; + } + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } + public int BrandID { get; set; } + public int CategoryID { get; set; } + public string? Brand { get; set; } + public string? Category { get; set; } + public int UserRole { get; set; } + public bool? TaskStatus { get; set; } + public SelectList? BrandSelectList { get; set; } + public SelectList? CategorySelectList { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditCompetencyRoleRequirementsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditCompetencyRoleRequirementsViewModel.cs new file mode 100644 index 0000000000..02aadc11c3 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditCompetencyRoleRequirementsViewModel.cs @@ -0,0 +1,31 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; +using DigitalLearningSolutions.Web.Models; +using System.Collections.Generic; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class EditCompetencyRoleRequirementsViewModel + { + public EditCompetencyRoleRequirementsViewModel( + CompetencyAssessmentBase competencyAssessmentBase, + List groupedCompetencyWithAssessmentRoleRequirements + ) + { + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + Id = competencyAssessmentBase.ID; + GroupedCompetencyWithAssessmentRoleRequirements = groupedCompetencyWithAssessmentRoleRequirements; + } + public int Id { get; set; } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public List GroupedCompetencyWithAssessmentRoleRequirements { get; set; } + + } +} + diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditDescriptionViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditDescriptionViewModel.cs new file mode 100644 index 0000000000..fcb36e370f --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditDescriptionViewModel.cs @@ -0,0 +1,22 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class EditDescriptionViewModel + { + public EditDescriptionViewModel() { } + public EditDescriptionViewModel(CompetencyAssessmentBase competencyAssessmentBase, bool? taskStatus) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + Description = competencyAssessmentBase.Description; + UserRole = competencyAssessmentBase.UserRole; + TaskStatus = taskStatus; + } + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + public string? Description { get; set; } + public int UserRole { get; set; } + public bool? TaskStatus { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditQuestionResponseRoleRequirementsFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditQuestionResponseRoleRequirementsFormData.cs new file mode 100644 index 0000000000..08d253efc7 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditQuestionResponseRoleRequirementsFormData.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class EditQuestionResponseRoleRequirementsFormData + { + /// + /// Key = ResponseValue + /// Value = RAG level (1,2,3) or null + /// + [Required] + public Dictionary ResponseRoleRequirements { get; set; } + = new Dictionary(); + public bool ApplyToAll { get; set; } + public int Id { get; set; } + public int CompetencyId { get; set; } + public int AssessmentQuestionId { get; set; } + + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditQuestionResponseRoleRequirementsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditQuestionResponseRoleRequirementsViewModel.cs new file mode 100644 index 0000000000..14ed8190c6 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditQuestionResponseRoleRequirementsViewModel.cs @@ -0,0 +1,33 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; +using DigitalLearningSolutions.Web.Models; +using System.Collections.Generic; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class EditQuestionResponseRoleRequirementsViewModel : EditQuestionResponseRoleRequirementsFormData + { + public EditQuestionResponseRoleRequirementsViewModel( + CompetencyAssessmentBase competencyAssessmentBase, + List groupedCompetencyWithAssessmentRoleRequirements, + int countAssessmentQuestionInSelfAssessment, int competencyId, int assessmentQuestionId + ) + { + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + Id = competencyAssessmentBase.ID; + CompetencyId = competencyId; + AssessmentQuestionId = assessmentQuestionId; + GroupedCompetencyWithAssessmentRoleRequirements = groupedCompetencyWithAssessmentRoleRequirements; + CountAssessmentQuestionInSelfAssessment = countAssessmentQuestionInSelfAssessment; + } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public int CountAssessmentQuestionInSelfAssessment { get; set; } + public List GroupedCompetencyWithAssessmentRoleRequirements { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditRoleProfileLinksViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditRoleProfileLinksViewModel.cs new file mode 100644 index 0000000000..54ddd177b5 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditRoleProfileLinksViewModel.cs @@ -0,0 +1,43 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using Humanizer; + using System.Collections.Generic; + using System.Linq; + + public class EditRoleProfileLinksViewModel + { + public EditRoleProfileLinksViewModel() { } + public EditRoleProfileLinksViewModel(CompetencyAssessmentBase competencyAssessmentBase, IEnumerable professionalGroups, IEnumerable subGroups, IEnumerable roles, string actionName, bool? taskStatus) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + ProfessionalGroupId = competencyAssessmentBase.NRPProfessionalGroupID; + SubGroupId = competencyAssessmentBase.NRPSubGroupID; + RoleId = competencyAssessmentBase.NRPRoleID; + UserRole = competencyAssessmentBase.UserRole; + TaskStatus = taskStatus; + ProfessionalGroups = professionalGroups; + SubGroups = subGroups; + Roles = roles; + ActionName = actionName; + RoleName = Roles.FirstOrDefault(r => r.ID == RoleId)?.ProfileName ?? "Don't link assessment to a role."; + SubGroupName = SubGroups.FirstOrDefault(s => s.ID == SubGroupId)?.SubGroup ?? "Don't link assessment to a sub group."; + GroupName = ProfessionalGroups.FirstOrDefault(p => p.ID == ProfessionalGroupId)?.ProfessionalGroup ?? "Don't link assessment to a professional group."; + } + public IEnumerable ProfessionalGroups { get; set; } + public IEnumerable SubGroups { get; set; } + public IEnumerable Roles { get; set; } + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } + public string ActionName { get; set; } + public int? ProfessionalGroupId { get; set; } + public int? SubGroupId { get; set; } + public int? RoleId { get; set; } + public int UserRole { get; set; } + public bool? TaskStatus { get; set; } + public string? GroupName { get; set; } + public string? SubGroupName { get; set; } + public string? RoleName { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditRoleRequirementsFlagsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditRoleRequirementsFlagsViewModel.cs new file mode 100644 index 0000000000..660153133b --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditRoleRequirementsFlagsViewModel.cs @@ -0,0 +1,26 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class EditRoleRequirementsFlagsViewModel : ManageCompetencyRoleRequirementsFormData + { + public EditRoleRequirementsFlagsViewModel( + CompetencyAssessmentBase competencyAssessmentBase + ) + { + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + TaskStatus = false; + Id = competencyAssessmentBase.ID; + EnforceRoleRequirementsForSignOff = competencyAssessmentBase.EnforceRoleRequirementsForSignOff; + IncludeRequirementsFilters = competencyAssessmentBase.IncludeRequirementsFilters; + } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditVocabularyViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditVocabularyViewModel.cs new file mode 100644 index 0000000000..c68c90f85e --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/EditVocabularyViewModel.cs @@ -0,0 +1,24 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using System.ComponentModel.DataAnnotations; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class EditVocabularyViewModel + { + public EditVocabularyViewModel() { } + public EditVocabularyViewModel(CompetencyAssessmentBase competencyAssessmentBase, bool? taskStatus) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + Vocabulary = competencyAssessmentBase.Vocabulary; + UserRole = competencyAssessmentBase.UserRole; + TaskStatus = taskStatus; + } + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + [Required(ErrorMessage = "Select a vocabulary option")] + public string Vocabulary { get; set; } + public int UserRole { get; set; } + public bool? TaskStatus { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/LearnerSignoffDeclarationViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/LearnerSignoffDeclarationViewModel.cs new file mode 100644 index 0000000000..66f2bc0d0e --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/LearnerSignoffDeclarationViewModel.cs @@ -0,0 +1,12 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class LearnerSignoffDeclarationViewModel : BaseSignoffDeclarationViewModel + { + public LearnerSignoffDeclarationViewModel() : base() { } + public LearnerSignoffDeclarationViewModel(int id) : base(id) { } + public LearnerSignoffDeclarationViewModel(LearnerSignoffDeclarationViewModel model) + { + CopyProperties(model); + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyAssessmentViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyAssessmentViewModel.cs new file mode 100644 index 0000000000..f62cf5d764 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyAssessmentViewModel.cs @@ -0,0 +1,34 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ManageCompetencyAssessmentViewModel + { + public ManageCompetencyAssessmentViewModel( + CompetencyAssessmentBase competencyAssessmentBase, + CompetencyAssessmentTaskStatus competencyAssessmentTaskStatus, + bool hasCompetencies + ) + { + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + PublishStatusID = competencyAssessmentBase.PublishStatusID; + UserRole = competencyAssessmentBase.UserRole; + CompetencyAssessmentTaskStatus = competencyAssessmentTaskStatus; + HasCompetencies = hasCompetencies; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + SelfAssessmentReviewID = competencyAssessmentBase.SelfAssessmentReviewID; + SelfAssessmentCommentID = competencyAssessmentBase.SelfAssessmentCommentID; + } + public string CompetencyAssessmentName { get; set; } + public int PublishStatusID { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public CompetencyAssessmentTaskStatus CompetencyAssessmentTaskStatus { get; set; } + public int? SelfAssessmentReviewID { get; set; } + public int? SelfAssessmentCommentID { get; set; } + public bool HasCompetencies { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyRoleRequirementsFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyRoleRequirementsFormData.cs new file mode 100644 index 0000000000..4bc9a981a0 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyRoleRequirementsFormData.cs @@ -0,0 +1,10 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ManageCompetencyRoleRequirementsFormData + { + public int Id { get; set; } + public bool? TaskStatus { get; set; } + public bool EnforceRoleRequirementsForSignOff { get; set; } + public bool IncludeRequirementsFilters { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyRoleRequirementsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyRoleRequirementsViewModel.cs new file mode 100644 index 0000000000..d0d7168596 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManageCompetencyRoleRequirementsViewModel.cs @@ -0,0 +1,38 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; +using DigitalLearningSolutions.Web.Models; +using System.Collections.Generic; +using System.Linq; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ManageCompetencyRoleRequirementsViewModel : ManageCompetencyRoleRequirementsFormData + { + public ManageCompetencyRoleRequirementsViewModel( + CompetencyAssessmentBase competencyAssessmentBase, + List groupedCompetencyWithAssessmentRoleRequirements, + CompetencyAssessmentTaskStatus taskStatus + ) + { + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + EnforceRoleRequirementsForSignOff = competencyAssessmentBase.EnforceRoleRequirementsForSignOff; + IncludeRequirementsFilters = competencyAssessmentBase.IncludeRequirementsFilters; + Id = competencyAssessmentBase.ID; + TaskStatus = taskStatus.RoleRequirementsTaskStatus; + CountCompetencyRequirements = + groupedCompetencyWithAssessmentRoleRequirements + .SelectMany(g => g.Competencies) + .SelectMany(c => c.Questions) + .Count(q => q.Responses.Any(r => r.LevelRAG != null)); + } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public int CountCompetencyRequirements { get; set; } + + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManagesupervisionViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManagesupervisionViewModel.cs new file mode 100644 index 0000000000..9a2b783f6e --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManagesupervisionViewModel.cs @@ -0,0 +1,70 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ManagesupervisionViewModel + { + public ManagesupervisionViewModel() { } + public ManagesupervisionViewModel(SupervisedSelfAssessmentSignoffViewModel signoff) + { + Signoff = signoff; + CompetencyAssessmentName = signoff.CompetencyAssessmentName; + } + public ManagesupervisionViewModel(SupervisorSignoffDeclarationViewModel supervisorDeclaration, + SupervisedSelfAssessmentSignoffViewModel signoff) + { + SupervisorDeclaration = supervisorDeclaration; + Signoff = signoff; + } + public ManagesupervisionViewModel(LearnerSignoffDeclarationViewModel learnerDeclaration, + SupervisorSignoffDeclarationViewModel supervisorDeclaration, + SupervisedSelfAssessmentSignoffViewModel signoff) + { + LearnerDeclaration = learnerDeclaration; + SupervisorDeclaration = supervisorDeclaration; + Signoff = signoff; + } + public ManagesupervisionViewModel(int competencyAssessmentId, + ManagesupervisionViewModel model, + string learnerDefaultText, + string supervisorDefaultText) + { + CompetencyAssessmentId = competencyAssessmentId; + CompetencyAssessmentName = model.CompetencyAssessmentName; + Signoff = model.Signoff; + SupervisorDeclaration = model.SupervisorDeclaration; + SupervisorDeclaration.DefaultText = supervisorDefaultText.Replace("{{CompetencyAssessmentName}}", CompetencyAssessmentName); + LearnerDeclaration = model.LearnerDeclaration; + LearnerDeclaration.DefaultText = learnerDefaultText.Replace("{{CompetencyAssessmentName}}", CompetencyAssessmentName); + } + public ManagesupervisionViewModel(int competencyAssessmentId, string competencyAssessmentName, + bool supervisorResultsReview, + bool SupervisorSelfAssessmentReview, + string? signOffSupervisorStatement, + string? signOffRequestorStatement, + string learnerDefaultText, + string supervisorDefaultText) + { + CompetencyAssessmentId = competencyAssessmentId; + CompetencyAssessmentName = competencyAssessmentName; + Signoff.CompetencyAssessmentId = competencyAssessmentId; + Signoff.Signoff = SupervisorSelfAssessmentReview == true ? 1 : 0; + Signoff.Confirm = supervisorResultsReview == true ? 1 : 0; + SupervisorDeclaration.CompetencyAssessmentName = competencyAssessmentName; + SupervisorDeclaration.CompetencyAssessmentId = competencyAssessmentId; + SupervisorDeclaration.DeclarationValue = signOffSupervisorStatement == null ? 0 : 1; + SupervisorDeclaration.CustomText = signOffSupervisorStatement; + SupervisorDeclaration.DefaultText = supervisorDefaultText.Replace("{{CompetencyAssessmentName}}", CompetencyAssessmentName); + LearnerDeclaration.DeclarationValue = signOffRequestorStatement == null ? 0 : 1; + LearnerDeclaration.CustomText = signOffRequestorStatement; + LearnerDeclaration.DefaultText = learnerDefaultText.Replace("{{CompetencyAssessmentName}}", CompetencyAssessmentName); + LearnerDeclaration.CompetencyAssessmentName = competencyAssessmentName; + LearnerDeclaration.CompetencyAssessmentId = competencyAssessmentId; + } + public SupervisedSelfAssessmentSignoffViewModel Signoff { get; set; } = new SupervisedSelfAssessmentSignoffViewModel(); + public SupervisorSignoffDeclarationViewModel SupervisorDeclaration { get; set; } = new SupervisorSignoffDeclarationViewModel(); + public LearnerSignoffDeclarationViewModel LearnerDeclaration { get; set; } = new LearnerSignoffDeclarationViewModel(); + public bool? TaskCompleteChecked { get; set; } + public int CompetencyAssessmentId { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/MyCompetencyAssessmentsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/MyCompetencyAssessmentsViewModel.cs new file mode 100644 index 0000000000..ec2cf74500 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/MyCompetencyAssessmentsViewModel.cs @@ -0,0 +1,43 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using System.Collections.Generic; + using System.Linq; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Web.Helpers; + using Microsoft.AspNetCore.Mvc.Rendering; + + public class MyCompetencyAssessmentsViewModel : BaseCompetencyAssessmentsPageViewModel + { + public readonly IEnumerable CompetencyAssessments; + public readonly bool IsWorkforceManager; + + public override SelectList CompetencyAssessmentSortByOptions { get; } = new SelectList(new[] + { + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentName, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentOwner, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentCreatedDate, + CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentPublishStatus + }); + + public MyCompetencyAssessmentsViewModel( + IEnumerable competencyAssessments, // Renamed parameter to avoid shadowing + string? searchString, + string sortBy, + string sortDirection, + int page, + bool isWorkforceManager + ) : base(searchString, sortBy, sortDirection, page) + { + var sortedItems = SortingHelper.SortCompetencyAssessmentItems( + competencyAssessments, + sortBy, + sortDirection + ); + var filteredItems = SearchHelper.FilterCompetencyAssessments(sortedItems, SearchString, 60, false).ToList(); + MatchingSearchResults = filteredItems.Count; + SetTotalPages(); + CompetencyAssessments = PaginateItems(filteredItems).Cast(); // Assign to the field + IsWorkforceManager = isWorkforceManager; + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/OptionsLabelsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/OptionsLabelsViewModel.cs new file mode 100644 index 0000000000..b1e3cd3a9a --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/OptionsLabelsViewModel.cs @@ -0,0 +1,62 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Web.Helpers; + using System.Collections.Generic; + + public class OptionsLabelsViewModel + { + public OptionsLabelsViewModel() + { + } + public OptionsLabelsViewModel(OptionsLabelsViewModel optionsLabels) + { + CompetencyAssessmentID = optionsLabels.CompetencyAssessmentID; + CurrentStep = optionsLabels.CurrentStep; + IncludeLearnerDeclarationPrompt = optionsLabels.IncludeLearnerDeclarationPrompt; + IncludesSignposting = optionsLabels.IncludesSignposting; + LinearNavigation = optionsLabels.LinearNavigation; + UseDescriptionExpanders = optionsLabels.UseDescriptionExpanders; + QuestionLabel = optionsLabels.QuestionLabel; + QuestionLabelText = optionsLabels.QuestionLabelText; + ReviewerCommentsLabel = optionsLabels.ReviewerCommentsLabel; + ReviewerCommentsLabelText = optionsLabels.ReviewerCommentsLabelText; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(optionsLabels.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(optionsLabels.Vocabulary); + SelfAssessmentOptionsTaskStatus = optionsLabels.SelfAssessmentOptionsTaskStatus; + IsSupervisionSwitchedOn = optionsLabels.IsSupervisionSwitchedOn; + IsSignpostedLearning = optionsLabels.IsSignpostedLearning; + } + + public int FrameworkId { get; set; } + public int CompetencyAssessmentID { get; set; } + public int CurrentStep { get; set; } = 1; + + public bool IncludeLearnerDeclarationPrompt { get; set; } + public bool IncludesSignposting { get; set; } + public bool LinearNavigation { get; set; } + public bool UseDescriptionExpanders { get; set; } + public bool QuestionLabel { get; set; } + public string? QuestionLabelText { get; set; } + public bool ReviewerCommentsLabel { get; set; } + public string? ReviewerCommentsLabelText { get; set; } + public bool IsSupervisionSwitchedOn { get; set; } + public bool IsSignpostedLearning { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public string? Vocabulary { get; set; } + public bool? SelfAssessmentOptionsTaskStatus { get; set; } + public bool Error { get; set; } + + } + public enum OptionLabel + { + Declaration = 1, + Signposting, + LinearNavigation, + DescriptionExpanders, + QuestionLabels, + CommentsLabel, + Summary + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishReviewViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishReviewViewModel.cs new file mode 100644 index 0000000000..5ca2d9d2b3 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishReviewViewModel.cs @@ -0,0 +1,34 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; +using System.Collections.Generic; +using System.Linq; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class PublishReviewViewModel + { + public PublishReviewViewModel() + { } + public PublishReviewViewModel(int competencyAssessmentID, string competencyAssessmentName, IEnumerable? selfAssessmentReviews, CompetencyAssessmentBase competencyAssessmentBase) + { + CompetencyAssessmentID = competencyAssessmentID; + CompetencyAssessmentName = competencyAssessmentName; + SelfAssessmentReviews = selfAssessmentReviews; + CompetencyAssessmentBase = competencyAssessmentBase; + CanPublish = selfAssessmentReviews?.All(x => x.SignedOff) ?? true; + } + public int CompetencyAssessmentID { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + public IEnumerable? SelfAssessmentReviews { get; set; } + public CompetencyAssessmentBase CompetencyAssessmentBase { get; set; } + public bool CanPublish { get; set; } + public string VocabSingular() + { + return FrameworkVocabularyHelper.VocabularySingular(CompetencyAssessmentBase.Vocabulary); + } + public string VocabPlural() + { + return FrameworkVocabularyHelper.VocabularyPlural(CompetencyAssessmentBase.Vocabulary); + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishWithoutReviewViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishWithoutReviewViewModel.cs new file mode 100644 index 0000000000..f8d6803fdb --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishWithoutReviewViewModel.cs @@ -0,0 +1,20 @@ +using DigitalLearningSolutions.Web.Attributes; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class PublishWithoutReviewViewModel + { + public PublishWithoutReviewViewModel() + { } + public PublishWithoutReviewViewModel(int competencyAssessmentID, string competencyAssessmentName) + { + CompetencyAssessmentID = competencyAssessmentID; + CompetencyAssessmentName = competencyAssessmentName; + } + public int CompetencyAssessmentID { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + public int AdminID { get; set; } + [BooleanMustBeTrue(ErrorMessage = "You must confirm that you wish to publish without review")] + public bool Confirm { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/RoleRequirementQuestionCellsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/RoleRequirementQuestionCellsViewModel.cs new file mode 100644 index 0000000000..387d64b63f --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/RoleRequirementQuestionCellsViewModel.cs @@ -0,0 +1,11 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Web.Models; + public class RoleRequirementQuestionCellsViewModel + { + public AssessmentQuestionModel AssessmentQuestion { get; set; } + public int AssessmentID { get; set; } + public int CompetencyID { get; set; } + public int AssessmentQuestionID { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectFrameworkSourcesFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectFrameworkSourcesFormData.cs new file mode 100644 index 0000000000..ae98be8ef2 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectFrameworkSourcesFormData.cs @@ -0,0 +1,13 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + public class SelectFrameworkSourcesFormData + { + [Required(ErrorMessage = "Select a framework")] + public int? FrameworkId { get; set; } + public int CompetencyAssessmentId { get; set; } + public bool? TaskStatus { get; set; } + public string? ActionName { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectFrameworkSourcesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectFrameworkSourcesViewModel.cs new file mode 100644 index 0000000000..8d1adea226 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectFrameworkSourcesViewModel.cs @@ -0,0 +1,40 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Data.Models.Frameworks; +using System.Collections.Generic; +using System.Linq; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SelectFrameworkSourcesViewModel : SelectFrameworkSourcesFormData + { + public SelectFrameworkSourcesViewModel() { } + public SelectFrameworkSourcesViewModel(CompetencyAssessmentBase competencyAssessmentBase, IEnumerable frameworks, int[] additionalFrameworksIds, int? primaryFramework, bool? taskStatus, string actionName) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + UserRole = competencyAssessmentBase.UserRole; + TaskStatus = taskStatus; + PrimaryFramework = frameworks.FirstOrDefault(f => f.ID == primaryFramework); + var excludedIds = new HashSet(additionalFrameworksIds); + if (primaryFramework.HasValue) + { + excludedIds.Add(primaryFramework.Value); + } + Frameworks = [.. frameworks + .Where(f => !excludedIds.Contains(f.ID)) + .OrderBy(f => f.FrameworkName)]; + AdditionalFrameworks = [.. additionalFrameworksIds.Select(id => frameworks.First(f => f.ID == id))]; + ActionName = actionName; + } + public IEnumerable Frameworks { get; set; } + public IEnumerable AdditionalFrameworks { get; set; } + public BrandedFramework? PrimaryFramework { get; set; } + public IEnumerable Roles { get; set; } + public int ID { get; set; } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string? GroupName { get; set; } + public string? SubGroupName { get; set; } + public string? RoleName { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectOptionalCompetenciesFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectOptionalCompetenciesFormData.cs new file mode 100644 index 0000000000..371dc7b1e8 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectOptionalCompetenciesFormData.cs @@ -0,0 +1,14 @@ +using DigitalLearningSolutions.Web.Helpers; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SelectOptionalCompetenciesFormData + { + public int ID { get; set; } + public bool? TaskStatus { get; set; } + public int? MinimumOptionalCompetencies { get; set; } + public string? ManageOptionalCompetenciesPrompt { get; set; } + public int[] SelectedCompetencyIds { get; set; } = []; + public int[] GroupIds { get; set; } = []; + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectOptionalCompetenciesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectOptionalCompetenciesViewModel.cs new file mode 100644 index 0000000000..351758007d --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SelectOptionalCompetenciesViewModel.cs @@ -0,0 +1,29 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Web.Helpers; + using System.Collections.Generic; + using System.Linq; + + public class SelectOptionalCompetenciesViewModel : SelectOptionalCompetenciesFormData + { + public SelectOptionalCompetenciesViewModel(CompetencyAssessmentBase competencyAssessmentBase, IEnumerable competencies, bool? taskStatus) + { + ID = competencyAssessmentBase.ID; + TaskStatus = taskStatus; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + MinimumOptionalCompetencies = competencyAssessmentBase.MinimumOptionalCompetencies; + ManageOptionalCompetenciesPrompt = competencyAssessmentBase.ManageOptionalCompetenciesPrompt; + UserRole = competencyAssessmentBase.UserRole; + CompetencyGroups = competencies.GroupBy(competency => competency.GroupName); + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + SelectedCompetencyIds = competencies.Where(c => c.Optional == true).Select(c => c.CompetencyID).ToArray(); + } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public IEnumerable>? CompetencyGroups { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SendForReviewViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SendForReviewViewModel.cs new file mode 100644 index 0000000000..96df300a3e --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SendForReviewViewModel.cs @@ -0,0 +1,29 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using System.Collections.Generic; +using System.Linq; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SendForReviewViewModel + { + public SendForReviewViewModel() + { } + public SendForReviewViewModel(int competencyAssessmentID, + string competencyAssessmentName, + IEnumerable? collaborators, + int publishStatusID) + { + CompetencyAssessmentID = competencyAssessmentID; + CompetencyAssessmentName = competencyAssessmentName; + Collaborators = collaborators.Where(x => x.CompetencyAssessmentRole != "Owner") ?? Enumerable.Empty(); + PublishStatusID = publishStatusID; + } + public int CompetencyAssessmentID { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + public IEnumerable? Collaborators { get; set; } + public int AdminID { get; set; } + public int PublishStatusID { get; set; } + public List UserChecked { get; set; } = new(); + public List SignOffRequiredChecked { get; set; } = new(); + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetMinimumOptionalCompetenciesFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetMinimumOptionalCompetenciesFormData.cs new file mode 100644 index 0000000000..a4ba09c392 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetMinimumOptionalCompetenciesFormData.cs @@ -0,0 +1,12 @@ +using DigitalLearningSolutions.Web.Helpers; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SetMinimumOptionalCompetenciesFormData + { + [MaxOptionalCount(nameof(OptionalCompetenciesCount))] + public int? MinimumOptionalCompetencies { get; set; } + public int OptionalCompetenciesCount { get; set; } + public int ID { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetMinimumOptionalCompetenciesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetMinimumOptionalCompetenciesViewModel.cs new file mode 100644 index 0000000000..a31fd2b25c --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetMinimumOptionalCompetenciesViewModel.cs @@ -0,0 +1,25 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Web.Helpers; + using System.Collections.Generic; + using System.Linq; + + public class SetMinimumOptionalCompetenciesViewModel : SetMinimumOptionalCompetenciesFormData + { + public SetMinimumOptionalCompetenciesViewModel(CompetencyAssessmentBase competencyAssessmentBase, IEnumerable competencies) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + MinimumOptionalCompetencies = competencyAssessmentBase.MinimumOptionalCompetencies; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + OptionalCompetenciesCount = competencies.Count(c => c.Optional == true); + } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetOptionalCompetencyLearnerPromptFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetOptionalCompetencyLearnerPromptFormData.cs new file mode 100644 index 0000000000..384dbaa8e4 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetOptionalCompetencyLearnerPromptFormData.cs @@ -0,0 +1,11 @@ +using DigitalLearningSolutions.Web.Helpers; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SetOptionalCompetencyLearnerPromptFormData + { + + public string? ManageOptionalCompetenciesPrompt { get; set; } + public int ID { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetOptionalCompetencyLearnerPromptViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetOptionalCompetencyLearnerPromptViewModel.cs new file mode 100644 index 0000000000..82e8d3c09b --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SetOptionalCompetencyLearnerPromptViewModel.cs @@ -0,0 +1,24 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using DigitalLearningSolutions.Web.Helpers; + using System.Collections.Generic; + using System.Linq; + + public class SetOptionalCompetencyLearnerPromptViewModel : SetOptionalCompetencyLearnerPromptFormData + { + public SetOptionalCompetencyLearnerPromptViewModel(CompetencyAssessmentBase competencyAssessmentBase, IEnumerable competencies) + { + ID = competencyAssessmentBase.ID; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + ManageOptionalCompetenciesPrompt = competencyAssessmentBase.ManageOptionalCompetenciesPrompt; + UserRole = competencyAssessmentBase.UserRole; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + } + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SubmitReviewViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SubmitReviewViewModel.cs new file mode 100644 index 0000000000..e81d183a9d --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SubmitReviewViewModel.cs @@ -0,0 +1,19 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SubmitReviewViewModel + { + public SubmitReviewViewModel() + { } + public SubmitReviewViewModel(int competencyAssessmentID, string competencyAssessmentName, SelfAssessmentReview review) + { + CompetencyAssessmentID = competencyAssessmentID; + CompetencyAssessmentName = competencyAssessmentName; + SelfAssessmentReview = review; + } + public int CompetencyAssessmentID { get; set; } + public string? CompetencyAssessmentName { get; set; } + public SelfAssessmentReview SelfAssessmentReview { get; set; } = new(); + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SupervisedSelfAssessmentSignoffViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SupervisedSelfAssessmentSignoffViewModel.cs new file mode 100644 index 0000000000..7a17b381a3 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SupervisedSelfAssessmentSignoffViewModel.cs @@ -0,0 +1,28 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SupervisedSelfAssessmentSignoffViewModel + { + public SupervisedSelfAssessmentSignoffViewModel() + { } + public SupervisedSelfAssessmentSignoffViewModel(int id, string competencyAssessmentName, string? actionName) + { + CompetencyAssessmentId = id; + CompetencyAssessmentName = competencyAssessmentName; + this.ActionName = actionName; + } + public SupervisedSelfAssessmentSignoffViewModel(SupervisedSelfAssessmentSignoffViewModel model) + { + CompetencyAssessmentId = model.CompetencyAssessmentId; + Confirm = model.Confirm; + Signoff = model.Signoff; + ActionName = model.ActionName; + } + public int CompetencyAssessmentId { get; set; } + public string CompetencyAssessmentName { get; set; } = string.Empty; + public int? Signoff { get; set; } + public int? Confirm { get; set; } + public string? ActionName { get; set; } + public string SignoffText => Signoff == 1 ? "Yes" : "No"; + public string ConfirmText => Confirm == 1 ? "Yes" : "No"; + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SupervisorSignoffDeclarationViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SupervisorSignoffDeclarationViewModel.cs new file mode 100644 index 0000000000..bdc4a7222a --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/SupervisorSignoffDeclarationViewModel.cs @@ -0,0 +1,13 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class SupervisorSignoffDeclarationViewModel : BaseSignoffDeclarationViewModel + { + public SupervisorSignoffDeclarationViewModel() : base() { } + + public SupervisorSignoffDeclarationViewModel(int id) : base(id) { } + public SupervisorSignoffDeclarationViewModel(SupervisorSignoffDeclarationViewModel model) + { + CopyProperties(model); + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ViewSelectedCompetenciesFormData.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ViewSelectedCompetenciesFormData.cs new file mode 100644 index 0000000000..467a9845e1 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ViewSelectedCompetenciesFormData.cs @@ -0,0 +1,8 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + public class ViewSelectedCompetenciesFormData + { + public int ID { get; set; } + public bool? TaskStatus { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ViewSelectedCompetenciesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ViewSelectedCompetenciesViewModel.cs new file mode 100644 index 0000000000..dc03de2eba --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ViewSelectedCompetenciesViewModel.cs @@ -0,0 +1,46 @@ +using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +using DigitalLearningSolutions.Web.Helpers; +using System.Collections.Generic; +using System.Linq; + +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + + public class ViewSelectedCompetenciesViewModel : ViewSelectedCompetenciesFormData + { + public ViewSelectedCompetenciesViewModel() { } + public ViewSelectedCompetenciesViewModel(CompetencyAssessmentBase competencyAssessmentBase, IEnumerable competencies, IEnumerable linkedFrameworks, bool? taskStatus) + { + ID = competencyAssessmentBase.ID; + TaskStatus = taskStatus; + CompetencyAssessmentName = competencyAssessmentBase.CompetencyAssessmentName; + UserRole = competencyAssessmentBase.UserRole; + Competencies = competencies; + VocabularySingular = FrameworkVocabularyHelper.VocabularySingular(competencyAssessmentBase.Vocabulary); + VocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(competencyAssessmentBase.Vocabulary); + var competencyCounts = competencies + .GroupBy(c => c.FrameworkId) + .ToDictionary(g => g.Key, g => g.Count()); + // Populate LinkedFrameworks with the relevant count + foreach (var framework in linkedFrameworks) + { + if (competencyCounts.TryGetValue(framework.ID, out var count)) + { + framework.AssessmentFrameworkCompetencyCount = count; + } + else + { + framework.AssessmentFrameworkCompetencyCount = 0; + } + } + LinkedFrameworks = linkedFrameworks; + } + + public string CompetencyAssessmentName { get; set; } + public int UserRole { get; set; } + public string VocabularySingular { get; set; } + public string VocabularyPlural { get; set; } + public IEnumerable Competencies { get; set; } + public IEnumerable LinkedFrameworks { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/WorkingGroupCollaboratorsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/WorkingGroupCollaboratorsViewModel.cs new file mode 100644 index 0000000000..6af2e05f64 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/WorkingGroupCollaboratorsViewModel.cs @@ -0,0 +1,17 @@ +namespace DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +{ + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; + using System.Collections.Generic; + + public class WorkingGroupCollaboratorsViewModel + { + public int CompetencyAssessmentID { get; set; } + public IEnumerable? Collaborators { get; set; } + public int AdminID { get; set; } + public bool? CompetencyAssessmentTaskStatus { get; set; } + public string? UserEmail { get; set; } + public bool Error { get; set; } + public string? ActionName { get; set; } + + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Frameworks/ChangeOwnerViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ChangeOwnerViewModel.cs new file mode 100644 index 0000000000..2780ea80be --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ChangeOwnerViewModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace DigitalLearningSolutions.Web.ViewModels.Frameworks +{ + public class ChangeOwnerViewModel + { + public int FrameworkId { get; set; } + public string? FrameworkName { get; set; } + + [Required] + [EmailAddress] + [Display(Name = "New Owner Email")] + public string NewOwnerEmail { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/AllRoleProfilesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/AllRoleProfilesViewModel.cs deleted file mode 100644 index bad699149f..0000000000 --- a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/AllRoleProfilesViewModel.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace DigitalLearningSolutions.Web.ViewModels.RoleProfiles -{ - using System.Collections.Generic; - using System.Linq; - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using DigitalLearningSolutions.Web.Helpers; - using Microsoft.AspNetCore.Mvc.Rendering; - public class AllRoleProfilesViewModel : BaseRoleProfilesPageViewModel - { - public readonly IEnumerable RoleProfiles; - public readonly bool IsWorkforceManager; - public override SelectList RoleProfileSortByOptions { get; } = new SelectList(new[] - { - RoleProfileSortByOptionTexts.RoleProfileName, - RoleProfileSortByOptionTexts.RoleProfileOwner, - RoleProfileSortByOptionTexts.RoleProfileCreatedDate, - RoleProfileSortByOptionTexts.RoleProfilePublishStatus, - RoleProfileSortByOptionTexts.RoleProfileBrand, - RoleProfileSortByOptionTexts.RoleProfileNationalRoleGroup, - RoleProfileSortByOptionTexts.RoleProfileNationalRoleProfile - }); - public AllRoleProfilesViewModel( - IEnumerable roleProfiles, - string? searchString, - string sortBy, - string sortDirection, - int page - ) : base(searchString, sortBy, sortDirection, page) - { - var sortedItems = SortingHelper.SortRoleProfileItems( - roleProfiles, - sortBy, - sortDirection - ); - var filteredItems = SearchHelper.FilterRoleProfiles(sortedItems, SearchString, 60, false).ToList(); - MatchingSearchResults = filteredItems.Count; - SetTotalPages(); - var paginatedItems = PaginateItems(filteredItems); - RoleProfiles = paginatedItems.Cast(); - } - } -} diff --git a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/MyRoleProfilesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/MyRoleProfilesViewModel.cs deleted file mode 100644 index 7657922974..0000000000 --- a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/MyRoleProfilesViewModel.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace DigitalLearningSolutions.Web.ViewModels.RoleProfiles -{ - using System.Collections.Generic; - using System.Linq; - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using DigitalLearningSolutions.Web.Helpers; - using Microsoft.AspNetCore.Mvc.Rendering; - public class MyRoleProfilesViewModel : BaseRoleProfilesPageViewModel - { - public readonly IEnumerable RoleProfiles; - public readonly bool IsWorkforceManager; - public override SelectList RoleProfileSortByOptions { get; } = new SelectList(new[] - { - RoleProfileSortByOptionTexts.RoleProfileName, - RoleProfileSortByOptionTexts.RoleProfileOwner, - RoleProfileSortByOptionTexts.RoleProfileCreatedDate, - RoleProfileSortByOptionTexts.RoleProfilePublishStatus - }); - public MyRoleProfilesViewModel( - IEnumerable roleProfiles, - string? searchString, - string sortBy, - string sortDirection, - int page, - bool isWorkforceManager - ) : base(searchString, sortBy, sortDirection, page) - { - var sortedItems = SortingHelper.SortRoleProfileItems( - roleProfiles, - sortBy, - sortDirection - ); - var filteredItems = SearchHelper.FilterRoleProfiles(sortedItems, SearchString, 60, false).ToList(); - MatchingSearchResults = filteredItems.Count; - SetTotalPages(); - var paginatedItems = PaginateItems(filteredItems); - RoleProfiles = paginatedItems.Cast(); - IsWorkforceManager = isWorkforceManager; - } - } -} diff --git a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/ProfessionalGroupViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/ProfessionalGroupViewModel.cs deleted file mode 100644 index 67701b8d15..0000000000 --- a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/ProfessionalGroupViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace DigitalLearningSolutions.Web.ViewModels.RoleProfiles -{ - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using System.Collections.Generic; - - public class ProfessionalGroupViewModel - { - public IEnumerable NRPProfessionalGroups { get; set; } - public RoleProfileBase RoleProfileBase { get; set; } - } -} diff --git a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/RoleProfilesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/RoleProfilesViewModel.cs deleted file mode 100644 index 9154eeee6b..0000000000 --- a/DigitalLearningSolutions.Web/ViewModels/RoleProfiles/RoleProfilesViewModel.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace DigitalLearningSolutions.Web.ViewModels.RoleProfiles -{ - using DigitalLearningSolutions.Web.Models.Enums; - using DigitalLearningSolutions.Web.ViewModels.Common; - - public class RoleProfilesViewModel - { - public RoleProfilesViewModel( - bool isWorkforceManager, - bool isWorkforceContributor, - AllRoleProfilesViewModel allRoleProfiles, - MyRoleProfilesViewModel myRoleProfiles, - RoleProfilesTab currentTab - ) - { - IsWorkforceManager = isWorkforceManager; - IsWorkforceContributor = isWorkforceContributor; - MyRoleProfilesViewModel = myRoleProfiles; - AllRoleProfilesViewModel = allRoleProfiles; - TabNavLinks = new TabsNavViewModel(currentTab); - } - - public bool IsWorkforceManager { get; set; } - public bool IsWorkforceContributor { get; set; } - public MyRoleProfilesViewModel MyRoleProfilesViewModel { get; set; } - public AllRoleProfilesViewModel AllRoleProfilesViewModel { get; set; } - public TabsNavViewModel TabNavLinks { get; set; } - } -} diff --git a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateOnProfileAssessmentViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateOnProfileAssessmentViewModel.cs index ed6e40e583..1b70330bbe 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateOnProfileAssessmentViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateOnProfileAssessmentViewModel.cs @@ -1,13 +1,13 @@ namespace DigitalLearningSolutions.Web.ViewModels.Supervisor { - using DigitalLearningSolutions.Data.Models.RoleProfiles; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; using DigitalLearningSolutions.Data.Models.SessionData.Supervisor; using DigitalLearningSolutions.Data.Models.Supervisor; using System.Collections.Generic; public class EnrolDelegateOnProfileAssessmentViewModel { - public SessionEnrolOnRoleProfile SessionEnrolOnRoleProfile { get; set; } + public SessionEnrolOnCompetencyAssessment SessionEnrolOnCompetencyAssessment { get; set; } public SupervisorDelegateDetail SupervisorDelegateDetail { get; set; } - public IEnumerable RoleProfiles { get; set; } + public IEnumerable CompetencyAssessments { get; set; } } } diff --git a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSetCompletByDateViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSetCompletByDateViewModel.cs index 935e257fb5..e5bca1fe3f 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSetCompletByDateViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSetCompletByDateViewModel.cs @@ -1,6 +1,6 @@ namespace DigitalLearningSolutions.Web.ViewModels.Supervisor { - using DigitalLearningSolutions.Data.Models.RoleProfiles; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; using DigitalLearningSolutions.Data.Models.Supervisor; using DigitalLearningSolutions.Web.Helpers; using System; @@ -8,7 +8,7 @@ public class EnrolDelegateSetCompletByDateViewModel { public SupervisorDelegateDetail SupervisorDelegateDetail { get; set; } - public RoleProfile RoleProfile { get; set; } + public CompetencyAssessment CompetencyAssessment { get; set; } public DateTime? CompleteByDate { get; set; } public OldDateValidator.ValidationResult? CompleteByValidationResult { get; set; } public bool ActionConfirmed { get; set; } diff --git a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSummaryViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSummaryViewModel.cs index ec238061e8..ad66e71ac8 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSummaryViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSummaryViewModel.cs @@ -1,13 +1,13 @@ namespace DigitalLearningSolutions.Web.ViewModels.Supervisor { - using DigitalLearningSolutions.Data.Models.RoleProfiles; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; using DigitalLearningSolutions.Data.Models.Supervisor; using System; public class EnrolDelegateSummaryViewModel { public SupervisorDelegateDetail SupervisorDelegateDetail { get; set; } - public RoleProfile RoleProfile { get; set; } + public CompetencyAssessment CompetencyAssessment { get; set; } public DateTime? CompleteByDate { get; set; } public string SupervisorRoleName { get; set; } public int SupervisorRoleCount { get; set; } diff --git a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSupervisorRoleViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSupervisorRoleViewModel.cs index 0c988b9457..4b15b66edf 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSupervisorRoleViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Supervisor/EnrolDelegateSupervisorRoleViewModel.cs @@ -1,6 +1,6 @@ namespace DigitalLearningSolutions.Web.ViewModels.Supervisor { - using DigitalLearningSolutions.Data.Models.RoleProfiles; + using DigitalLearningSolutions.Data.Models.CompetencyAssessments; using DigitalLearningSolutions.Data.Models.Supervisor; using System; using System.Collections.Generic; @@ -9,7 +9,7 @@ public class EnrolDelegateSupervisorRoleViewModel { public SupervisorDelegateDetail? SupervisorDelegateDetail { get; set; } - public RoleProfile? RoleProfile { get; set; } + public CompetencyAssessment? CompetencyAssessment { get; set; } [Required(ErrorMessage = "Please choose a supervisor role")] public int? SelfAssessmentSupervisorRoleId { get; set; } public IEnumerable? SelfAssessmentSupervisorRoles { get; set; } diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs index a1b67e6bf0..dc4da36717 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs @@ -6,17 +6,19 @@ public class SelfAssessmentReportsViewModel { public SelfAssessmentReportsViewModel( - IEnumerable selfAssessmentSelects, int? adminCategoryId, int categoryId, bool showTableauLink + IEnumerable selfAssessmentSelects, int? adminCategoryId, int categoryId, bool dSATreportIsPublish, bool showTableauLink ) { SelfAssessmentSelects = selfAssessmentSelects; AdminCategoryId = adminCategoryId; CategoryId = categoryId; + DSATreportIsPublish = dSATreportIsPublish; ShowTableauLink = showTableauLink; } public IEnumerable SelfAssessmentSelects { get; set; } public int? AdminCategoryId { get; set; } public int CategoryId { get; set; } + public bool DSATreportIsPublish { get; set; } public bool ShowTableauLink { get; set; } = false; } } diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/Shared/DelegateInfoViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/Shared/DelegateInfoViewModel.cs index 814e488ac9..588049ef6e 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/Shared/DelegateInfoViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/Shared/DelegateInfoViewModel.cs @@ -29,7 +29,8 @@ IEnumerable delegateRegistrationPrompts IsAdmin = delegateUser.IsAdmin; IsPasswordSet = delegateUser.IsPasswordSet; RegistrationType = delegateUser.RegistrationType; - + CentreEmail = delegateUser.Email; + PrimaryEmail = Guid.TryParse(delegateUser.PrimaryEmail, out _) ? null : delegateUser.PrimaryEmail; Email = delegateUser.EmailAddress; JobGroupId = delegateUser.JobGroupId; JobGroup = delegateUser.JobGroupName; @@ -62,6 +63,8 @@ IEnumerable delegateRegistrationPrompts public RegistrationType RegistrationType { get; set; } public string? Email { get; set; } + public string? CentreEmail { get; set; } + public string? PrimaryEmail { get; set; } public int JobGroupId { get; set; } public string? JobGroup { get; set; } public string? RegistrationDate { get; set; } diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetencies.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetencies.cshtml new file mode 100644 index 0000000000..119e3d17dd --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetencies.cshtml @@ -0,0 +1,119 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model AddCompetenciesViewModel; +@{ + ViewData["Title"] = "Add Competencies to Self Assessment"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} + +
+ +
+ +

+ Add @Model.VocabularyPlural.ToLower() to assessment from @Model.FrameworkName +

+
+ + @if (Model.GroupedCompetencies.Count() > 0) + { + @foreach (var competencyGroup in Model.GroupedCompetencies) + { + @if (competencyGroup.FrameworkCompetencies.Count() > 0) + { +
+

+ @competencyGroup.Name +

+ @if (competencyGroup.FrameworkCompetencies.Count() > 1) + { + + } +
+ @foreach (var competency in competencyGroup.FrameworkCompetencies) + { +
+ + +
+ } +
+
+ } + } + } + + @if (Model.UngroupedCompetencies.Any()) + { +

+ Ungrouped @Model.VocabularyPlural.ToLower() +

+ @if (Model.UngroupedCompetencies.Count() > 1) + { + + } + @foreach (var competency in Model.UngroupedCompetencies) + { + +
+ + +
+ + } + } + +
+
+ +
+@section scripts { + +} diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetenciesSelectFramework.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetenciesSelectFramework.cshtml new file mode 100644 index 0000000000..1bb44950b3 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetenciesSelectFramework.cshtml @@ -0,0 +1,54 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model AddCompetenciesSelectFrameworkViewModel; +@{ + ViewData["Title"] = "Select Framework Source"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Select Framework Source

+
+ @if (!ViewData.ModelState.IsValid) + { + + } +
+ +
+ +

+ Select the framework source you would like to use to add @Model.VocabularyPlural.ToLower() to this competency assessment. +

+
+
+ @foreach (var framework in Model.LinkedFrameworks) + { +
+ + +
+ } +
+
+ +
+ + + diff --git a/DigitalLearningSolutions.Web/Views/RoleProfiles/RoleProfile.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessment.cshtml similarity index 100% rename from DigitalLearningSolutions.Web/Views/RoleProfiles/RoleProfile.cshtml rename to DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessment.cshtml diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentFeatures.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentFeatures.cshtml new file mode 100644 index 0000000000..1e5ef6a629 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentFeatures.cshtml @@ -0,0 +1,64 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model CompetencyAssessmentFeaturesViewModel +@{ + ViewData["Title"] = "Competency assessment features "; + ViewData["Application"] = "Framework Service"; + ViewData["HeaderPathName"] = "Framework Service"; +} + +@section NavMenuItems { + +} + @section NavBreadcrumbs { + +} + +

Create a new competency assessment

+

Which features of the framework do you want to copy into the competency assessment?

+

The framework itself will be used as the source for this self-assessment.

+

Select any additional features you want to copy into the competency assessment. You can edit all features later if needed.

+
+ @if (!ViewData.ModelState.IsValid) + { + + } + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentOptions.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentOptions.cshtml new file mode 100644 index 0000000000..714c86c0cb --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentOptions.cshtml @@ -0,0 +1,415 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model OptionsLabelsViewModel +@{ + ViewData["Title"] = "Competency Assessment options and labels"; + ViewData["Application"] = "Competency Assessment Service"; + + var steps = new[] + { + new { Step = (int)OptionLabel.Declaration, Text = "Declaration"}, + new { Step = (int)OptionLabel.Signposting, Text = "Signposting"}, + new { Step = (int)OptionLabel.LinearNavigation, Text = "Linear navigation"}, + new { Step = (int)OptionLabel.DescriptionExpanders, Text = "Description expanders"}, + new { Step = (int)OptionLabel.QuestionLabels, Text = "Question label"}, + new { Step = (int)OptionLabel.CommentsLabel, Text = "Comments label"}, + new { Step = (int)OptionLabel.Summary, Text = "Summary"}, + }; + if (!Model.IsSupervisionSwitchedOn) + steps = steps.Where(s => s.Step != (int)OptionLabel.Declaration && s.Step != (int)OptionLabel.CommentsLabel).ToArray(); + if (!Model.IsSignpostedLearning) + steps = steps.Where(s => s.Step != (int)OptionLabel.Signposting).ToArray(); + + int index = Array.FindIndex(steps, s => s.Step == Model.CurrentStep); + + const string questionLabelId = "conditional-completion-criteria"; + var questionLabelCss = "nhsuk-radios__conditional" + (Model.QuestionLabel ? "" : " nhsuk-radios__conditional--hidden"); + const string reviewerCommentsLabelId = "conditional-completion-criteria"; + var reviewerCommentsLabelCss = "nhsuk-radios__conditional" + (Model.ReviewerCommentsLabel ? "" : " nhsuk-radios__conditional--hidden"); +} + +@section NavMenuItems { + +} +@if (Model.Error) +{ + +} + +@section NavBreadcrumbs { + +} + +
+ + + @if (Model.CurrentStep == (int)OptionLabel.Declaration) + { +
+
+ +

+ Display learner declaration? +

+
+
+ Do you want to prompt learners to agree to the self assessment process when they first start the self assessment? +
+
+
+ + +
+ +
+ + +
+
+
+
+ } + else if (Model.CurrentStep == (int)OptionLabel.Signposting) + { +
+
+ +

+ Include signposting? +

+
+
+ Do you want to display signposted learning content when a learner submits their self assessment results? +
+
+
+ + +
+ +
+ + +
+
+
+
+ } + else if (Model.CurrentStep == (int)OptionLabel.LinearNavigation) + { +
+
+ +

+ Use linear navigation? +

+
+
+ Do you want the learner to work through @Model.VocabularyPlural.ToLower() using next and previous links? If not, the learner will navigate between @Model.VocabularyPlural.ToLower() using the @Model.VocabularySingular.ToLower() menu. +
+
+
+ + +
+ +
+ + +
+
+
+
+ } + else if (Model.CurrentStep == (int)OptionLabel.DescriptionExpanders) + { +
+
+ +

+ Use description expanders? +

+
+
+ Do you want to show @Model.VocabularySingular.ToLower() descriptions using expanders? If not, the description will be displayed on the page without the user needing to expand it. +
+
+
+ + +
+ +
+ + +
+
+
+
+ } + else if (Model.CurrentStep == (int)OptionLabel.QuestionLabels) + { +
+
+ +

+ Custom question label +

+
+
+ Do you want to specify a custom label for self assessment questions? If not, they will be labelled as “question”. +
+
+
+ + +
+
+ +
+
+ + +
+
+
+
+ } + else if (Model.CurrentStep == (int)OptionLabel.CommentsLabel) + { +
+
+ +

+ Reviewer comments label +

+
+
+ Do you want to specify a custom supervisor comments prompt? If not, they will be labelled as “Reviewer comments”. +
+
+
+ + +
+
+ +
+
+ + +
+
+
+
+ } + else if (Model.CurrentStep == (int)OptionLabel.Summary) + { +
+
+ +
+

Summary

+
+ +
+ @if (Model.IsSupervisionSwitchedOn) + { +
+
+ Display learner declaration? +
+
+ @(Model.IncludeLearnerDeclarationPrompt ? "Yes" : "No") +
+
+ + Change + +
+
+ } + @if (Model.IsSignpostedLearning) + { +
+
+ Include signposting? +
+
+ @(Model.IncludesSignposting ? "Yes" : "No") +
+
+ + Change + +
+
+ } +
+
+ Use linear navigation? +
+
+ @(Model.LinearNavigation ? "Yes" : "No") +
+
+ + Change + +
+
+
+
+ Use description expanders? +
+
+ @(Model.UseDescriptionExpanders ? "Yes" : "No") +
+
+ + Change + +
+
+
+
+ Custom question label +
+
+ @(Model.QuestionLabel ? "Yes - " + Model.QuestionLabelText : "No") +
+
+ + Change + +
+
+ @if (Model.IsSupervisionSwitchedOn) + { +
+
+ Reviewer comments label +
+
+ @(Model.ReviewerCommentsLabel ? "Yes - " + Model.ReviewerCommentsLabelText : "No") +
+
+ + Change + +
+
+ } +
+
+
+ + } + @if (index > 0 && Model.CurrentStep < (int)OptionLabel.Summary) + { + + Back + + } + + + +
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentPreviewConfirm.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentPreviewConfirm.cshtml new file mode 100644 index 0000000000..ad81c18921 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentPreviewConfirm.cshtml @@ -0,0 +1,51 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@using Microsoft.Extensions.Configuration +@model CompetencyAssessmentPreviewViewModel +@inject IConfiguration Configuration + +@{ + ViewData["Title"] = "Competency Assessment - Preview"; + ViewData["Application"] = "Competency Assessment Service"; + ViewData["HeaderPathName"] = "Competency Assessment Service"; +} + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} +
+
+
+
+

+ Preview self assessment +

+

+ To preview this self assessment, it must be made available to your current centre, @Model.CentreName, if it isn’t already. You will then be enrolled as a test learner to view the self-assessment as a learner would. +

+
+ +
+
+ +
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentSummary.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentSummary.cshtml new file mode 100644 index 0000000000..be9f1d0e0d --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentSummary.cshtml @@ -0,0 +1,81 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model CompetencyAssessmentFeaturesViewModel +@{ + ViewData["Title"] = "Competency assessment features summary"; + ViewData["Application"] = "Framework Service"; + ViewData["HeaderPathName"] = "Framework Service"; +} + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} + + +

Check your answer before creating competency assessment

+
+ @if (!ViewData.ModelState.IsValid) + { + + } +
+
+ +
+
+ Assessment name +
+
+ @Model.CompetencyAssessmentName +
+
+ + Change assessment name + +
+
+
+
+ Features to copy from framework +
+
+ @if (Model.SelectedFeatures.Any()) + { +
    + @foreach (var feature in Model.SelectedFeatures) + { +
  • @feature
  • + } +
+ } + else + { + No features selected + } +
+
+ + Change features + +
+
+
+ + + + +
+ + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentWorkingGroup.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentWorkingGroup.cshtml new file mode 100644 index 0000000000..ea7d9be54d --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentWorkingGroup.cshtml @@ -0,0 +1,128 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model WorkingGroupCollaboratorsViewModel +@{ + ViewData["Title"] = "Assign working group"; + ViewData["Application"] = "Competency Assessment Service"; + ViewData["HeaderPathName"] = "Competency Assessment Service"; + string errMsg = TempData["CompetencyAssessmentError"]?.ToString(); + if (!string.IsNullOrWhiteSpace(errMsg)) { Model.Error = true; } +} + +@section NavMenuItems { + +} +@if (Model.Error) +{ + +} +@section NavBreadcrumbs { + +} +

Choose working group members

+
+

Working Group Members

+ + + + + + @if (Model.Collaborators.Count() > 1) + { + + } + + + + @foreach (var collaborator in Model.Collaborators) + { + + + + @if (Model.Collaborators.Count() > 1) + { + + } + + } + + +
+ +
+ +
+
+ + +
+ Provide the email address of a user with a registered DLS admin account to add as a Contributor (to help create your competency assessment) or Reviewer (to review your competency assessment and mark it as ready for publication). +
+ @if (Model.Error) + { + + Error: @errMsg + + } + +
+
+ + + +
+ @if (Model.ActionName == "CollaboratorReview") + { + + Done + + } + else + { + + } +
+
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmChangePrimaryFramework.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmChangePrimaryFramework.cshtml new file mode 100644 index 0000000000..3ef1327b76 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmChangePrimaryFramework.cshtml @@ -0,0 +1,43 @@ +@using DigitalLearningSolutions.Web.Helpers +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model ConfirmChangePrimaryFrameworkViewModel; +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["Title"] = "Competency Assessments - Change primary framework"; +} +
+
+ @if (errorHasOccurred) + { + + } + +

Change primary framework to @Model.FrameworkName

+
+
+
+
+
+ + + + + + + + + + + + +
+
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmMakePrimaryFramework.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmMakePrimaryFramework.cshtml new file mode 100644 index 0000000000..1d70a73cf7 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmMakePrimaryFramework.cshtml @@ -0,0 +1,121 @@ +@using DigitalLearningSolutions.Web.Helpers +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model ConfirmMakePrimaryFrameworkViewModel; + +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid && ViewData.ModelState.ContainsKey(nameof(Model.Confirm)); + ViewData["Title"] = "Competency Assessments - make primary framework"; +} + +
+
+ @if (errorHasOccurred) + { + + } +

Make @Model.FrameworkName primary framework

+
+
+ +
+
+
+ +
+ + Which features of the framework do you want to copy into this @Model.FrameworkName framework? + +
+
+ + +
+ The framework description will be copied into the competency assessment description. +
+
+
+
+
+ + +
+ The framework branding (provider and category) will be copied. +
+
+
+
+
+ + +
+ The framework vocabulary (such as proficiency, competency, or capability) will be copied. +
+
+
+ +
+
+ + +
+ Contributors and reviewers from the framework will be copied. +
+
+
+ +
+
+ + +
+ All competencies in the framework will be copied. +
+
+
+
+
+ + +
+ + + +
+
+ @* Hidden fields *@ + + + + + + + + + + +
+ + +
+
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmRemoveFrameworkSource.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmRemoveFrameworkSource.cshtml new file mode 100644 index 0000000000..a20a786820 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ConfirmRemoveFrameworkSource.cshtml @@ -0,0 +1,51 @@ +@using DigitalLearningSolutions.Web.Helpers +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model ConfirmRemoveFrameworkSourceViewModel; +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["Title"] = "Competency Assessments - Remove source framework"; +} +
+
+ @if (errorHasOccurred) + { + + } + +

Remove framework source from @Model.AssessmentName

+
+
+ + + +
+
+
+ + + + + + +

+ This competency assessment has @Model.CompetencyCount @DisplayStringHelper.PluraliseStringIfRequired(@Model.Vocabulary.ToLower(), Model.CompetencyCount) + associated with it from the framework @Model.FrameworkName. Removing this + framework source will remove the @DisplayStringHelper.PluraliseStringIfRequired(@Model.Vocabulary.ToLower(), Model.CompetencyCount) from the assessment. +

+ + + + + + +
+
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditBranding.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditBranding.cshtml new file mode 100644 index 0000000000..2230c98c3e --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditBranding.cshtml @@ -0,0 +1,82 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model EditBrandingViewModel +@{ + ViewData["Title"] = "Assessment Branding"; + ViewData["Application"] = "Framework Service"; + ViewData["HeaderPathName"] = "Framework Service"; +} + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} + +

Edit @Model.CompetencyAssessmentName provider and category

+

This will be visible to users when they browse assessments. It will help them to identify where the assessment is from and what it contains.

+
+ @if (!ViewData.ModelState.IsValid) + { + + } + + + +
+ + +
+
+ + + +
+ + +
+
+ + + + + + + + + + + +@section scripts { + +} diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditCompetencyRoleRequirements.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditCompetencyRoleRequirements.cshtml new file mode 100644 index 0000000000..73e7c2f74c --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditCompetencyRoleRequirements.cshtml @@ -0,0 +1,93 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model EditCompetencyRoleRequirementsViewModel; +@{ + ViewData["Title"] = "Manage Role Requirements"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Edit @Model.VocabularySingular.ToLower() role requirements

+

+ Manage role requirements @Model.CompetencyAssessmentName @Model.VocabularySingular.ToLower() self assessment questions. +

+@foreach (var group in Model.GroupedCompetencyWithAssessmentRoleRequirements) +{ +

@Model.VocabularySingular group: @group.GroupName

+ + + + + + + + + + + @foreach (var competency in group.Competencies) + { + + + @await Html.PartialAsync( + "~/Views/CompetencyAssessments/Shared/_RoleRequirementQuestionCells.cshtml", + new RoleRequirementQuestionCellsViewModel + { + AssessmentQuestion = competency.Questions.First(), + AssessmentID = Model.Id, + CompetencyID = competency.CompetencyID, + AssessmentQuestionID = competency.Questions.First().AssessmentQuestionID + } + ) + + @foreach (var question in competency.Questions.Skip(1)) + { + + @await Html.PartialAsync( + "~/Views/CompetencyAssessments/Shared/_RoleRequirementQuestionCells.cshtml", + new RoleRequirementQuestionCellsViewModel + { + AssessmentQuestion = question, + AssessmentID = Model.Id, + CompetencyID = competency.CompetencyID, + AssessmentQuestionID = question.AssessmentQuestionID + } + ) + + } + } + +
@Model.VocabularySingularAssessment Question Role RequirementsActions
+

+ @competency.Name +

+ @if (!string.IsNullOrWhiteSpace(competency.Description)) + { +

+ @(Html.Raw(competency.Description)) +

+ } +
+} + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditDescription.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditDescription.cshtml new file mode 100644 index 0000000000..37c7418c28 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditDescription.cshtml @@ -0,0 +1,60 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model EditDescriptionViewModel; +@{ + ViewData["Title"] = "Edit Introductory Text"; + ViewData["Application"] = "Framework Service"; +} + + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} + +

Edit @Model.CompetencyAssessmentName introductory text

+
+ @if (!ViewData.ModelState.IsValid) + { + + } + + + + + + + + + + +@section scripts { + +} diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditIncludeRequirementsFilters.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditIncludeRequirementsFilters.cshtml new file mode 100644 index 0000000000..453e9775b4 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditIncludeRequirementsFilters.cshtml @@ -0,0 +1,60 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model EditRoleRequirementsFlagsViewModel; +@{ + ViewData["Title"] = "Manage Role Requirements"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Edit include role requirements filters

+
+ +
+ +

Do you want to include role requirements filters in the @Model.CompetencyAssessmentName self assessment?

+
+
+ This will allow the learner to filter self assessment competencies based on whether they are meeting the role requirements set. +
+ +
+
+ + +
+
+ + +
+
+
+
+ + + + +
+ diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditQuestionResponseRoleRequirements.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditQuestionResponseRoleRequirements.cshtml new file mode 100644 index 0000000000..3d0a0aa099 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditQuestionResponseRoleRequirements.cshtml @@ -0,0 +1,161 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model EditQuestionResponseRoleRequirementsViewModel; +@{ + ViewData["Title"] = "Manage Role Requirements"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Edit @Model.VocabularySingular.ToLower() role requirements

+
+
+
+ @Model.VocabularySingular Assessment +
+
+ @Model.CompetencyAssessmentName +
+
+
+
+ @Model.VocabularySingular group +
+
+ @Model.GroupedCompetencyWithAssessmentRoleRequirements.First().GroupName +
+
+
+
+ @Model.VocabularySingular +
+
+

+ @Model.GroupedCompetencyWithAssessmentRoleRequirements.First().Competencies.First().Name +

+ @if (!string.IsNullOrWhiteSpace(Model.GroupedCompetencyWithAssessmentRoleRequirements.First().Competencies.First().Description)) + { +

+ @(Html.Raw(Model.GroupedCompetencyWithAssessmentRoleRequirements.First().Competencies.First().Description)) +

+ } +
+
+
+
+ Assessment Question +
+
+ @Model.GroupedCompetencyWithAssessmentRoleRequirements.First().Competencies.First().Questions.First().Question +
+
+
+
+

Set role requirements for each possible response

+ @foreach (var response in Model.GroupedCompetencyWithAssessmentRoleRequirements.First().Competencies.First().Questions.First().Responses) + { + +
+ + @response.ResponseLabel + +
+ Choose a role requirement status for learners choosing this response +
+ +
+
+ + +
+
+ + +
+
+ + +
+
or
+
+ + +
+
+
+
+ } + @if (Model.CountAssessmentQuestionInSelfAssessment > 1) + { +
+ + Apply to all? + + +
+ } + + + + +
+ diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditRoleProfileLinks.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditRoleProfileLinks.cshtml new file mode 100644 index 0000000000..388e75e848 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditRoleProfileLinks.cshtml @@ -0,0 +1,259 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments; +@model EditRoleProfileLinksViewModel; +@{ + ViewData["Title"] = "Edit National Role Profile Links"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} + +

Edit @Model.CompetencyAssessmentName national NHS role profile links

+@if (Model.TaskStatus != null | Model.ActionName != "EditGroup") +{ +
+ +
+
+ Professional group +
+
+ @Model.GroupName +
+
+ + Change professional group link + +
+
+ @if (Model.ProfessionalGroupId != null && Model.ActionName != "EditSubGroup") + { +
+
+ Sub group +
+
+ @Model.SubGroupName +
+
+ + Change sub group link + +
+
+ } + @if (Model.SubGroupId != null && Model.ActionName == "Summary") + { +
+
+ Role +
+
+ @Model.RoleName +
+
+ + Change role profile link + +
+
+ } +
+} +@if (Model.ActionName == "EditGroup") +{ +
+ @if (!ViewData.ModelState.IsValid) + { + + } + +
+ +

+ Choose the NHS national role profile professional group to associate with this assessment. +

+
+ +
+ @foreach (var professionalGroup in Model.ProfessionalGroups) + { +
+ + +
+ } +
+ +
+ + +
+
+
+
+ + + + + + + + + + + + + +} +else if (Model.ActionName == "EditSubGroup" && Model.ProfessionalGroupId != null) +{ +
+ @if (!ViewData.ModelState.IsValid) + { + + } + +
+ +

+ Choose the role sub group to associate with this assessment. +

+
+ +
+ @foreach (var subGroup in Model.SubGroups) + { +
+ + +
+ } +
+ +
+ + +
+
+
+
+ + + + + + + + + + + + +} +else if (Model.ActionName == "EditRole" && Model.SubGroupId != null) +{ +
+ @if (!ViewData.ModelState.IsValid) + { + + } + +
+ +

+ Choose the role to associate with this assessment. +

+
+ +
+ @foreach (var role in Model.Roles) + { +
+ + +
+ } +
+ +
+ + +
+
+
+
+ + + + + + + + + + + + +} +else if (Model.ActionName == "Summary") +{ +
+ + + + + + + + + + + + + + +
+} + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditVocabulary.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditVocabulary.cshtml new file mode 100644 index 0000000000..3b32db2a58 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditVocabulary.cshtml @@ -0,0 +1,98 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model EditVocabularyViewModel +@{ + ViewData["Title"] = "Assessment Branding"; + ViewData["Application"] = "Framework Service"; + ViewData["HeaderPathName"] = "Framework Service"; +} + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} + +

Edit @Model.CompetencyAssessmentName vocabulary

+
+ @if (!ViewData.ModelState.IsValid) + { + + } + +
+ +

+ Choose assessment vocabulary - the name given to the building blocks of your assessment. +

+
+
+
+ + +
+ The attributes (skills, knowledge and behaviours) which individuals bring to the workplace. +
+
+ +
+ + +
+ Defined standards of performance, focused on the outputs of work and observable performance. +
+
+
+ + +
+ The knowledge and skills that registered nurses must demonstrate when caring for people of all ages and across all care settings. +
+
+
+ + +
+ The same as capability | capabilities.
+ The term capabilities is recommended to make a clear distinction from competences. +
+
+
+
+ +
+ + + + + + + + + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EnforceRoleRequirementsForSignOff.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EnforceRoleRequirementsForSignOff.cshtml new file mode 100644 index 0000000000..081d6d7601 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EnforceRoleRequirementsForSignOff.cshtml @@ -0,0 +1,60 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model EditRoleRequirementsFlagsViewModel; +@{ + ViewData["Title"] = "Manage Role Requirements"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Edit role requirement enforcement

+
+ +
+ +

Do you want to enforce role requirements for the @Model.CompetencyAssessmentName self assessment?

+
+
+ When role requirements are enforced, learners can only request confirmation for self‑assessment results that meet the required competency criteria, even if no specific requirements are set. +
+ +
+
+ + +
+
+ + +
+
+
+
+ + + + +
+ diff --git a/DigitalLearningSolutions.Web/Views/RoleProfiles/Index.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Index.cshtml similarity index 65% rename from DigitalLearningSolutions.Web/Views/RoleProfiles/Index.cshtml rename to DigitalLearningSolutions.Web/Views/CompetencyAssessments/Index.cshtml index 89477e0ff7..0c6f91d80a 100644 --- a/DigitalLearningSolutions.Web/Views/RoleProfiles/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Index.cshtml @@ -1,8 +1,8 @@ -@using DigitalLearningSolutions.Web.ViewModels.RoleProfiles -@model RoleProfilesViewModel +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model CompetencyAssessmentsViewModel @{ - ViewData["Title"] = (string)ViewContext.RouteData.Values["tabname"] == "Mine" ? "My Role Profiles" : "All Role Profiles"; + ViewData["Title"] = (string)ViewContext.RouteData.Values["tabname"] == "Mine" ? "My Competency Assessments" : "All Competency Assessments"; ViewData["Application"] = "Framework Service"; } @@ -13,7 +13,7 @@ @@ -26,11 +26,11 @@
@if ((string)ViewContext.RouteData.Values["tabname"] == "Mine") { - + } else if ((string)ViewContext.RouteData.Values["tabname"] == "All") { - + }
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/LearnerSignoffDeclaration.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/LearnerSignoffDeclaration.cshtml new file mode 100644 index 0000000000..d1ea62cb3d --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/LearnerSignoffDeclaration.cshtml @@ -0,0 +1,109 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model LearnerSignoffDeclarationViewModel +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid && ViewData.ModelState.ContainsKey(nameof(Model.CustomText)); + ViewData["Title"] = "Learner sign off declaration"; +} + + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} +
+
+ @if (errorHasOccurred) + { + + } +
+
+ +

+ Learner sign off declaration +

+
+ +

+ This will be displayed to the learner is requesting supervisor signing off of their completed self assessment +

+
+
+ +
+ + + +
+
+ @Html.Raw(@Model.DefaultText) +
+
+ + +
+ +
+ + + +
+
+
+
+ + + +
+ + + + Back + + + +
+
+
+
+@section scripts { + +} diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyAssessment.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyAssessment.cshtml new file mode 100644 index 0000000000..5cfd64bf85 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyAssessment.cshtml @@ -0,0 +1,269 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model ManageCompetencyAssessmentViewModel +@{ + ViewData["Title"] = Model.PublishStatusID == 3 ? "Manage" : "Create" + " competency assessment"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} + +

@ViewData["Title"]

+ +
+
+ +
+
+ Assessment name +
+
+ @Model.CompetencyAssessmentName +
+
+ + Change assessment name + +
+
+
+
+ Publish status +
+
+ + @(Model.PublishStatusID == 1 ? "Draft" : Model.PublishStatusID == 2 ? "In review" : "Published") + +
+
+ @if (Model.SelfAssessmentReviewID != null && Model.SelfAssessmentCommentID is null) + { + + Submit my review review + + } +
+ @if (Model.HasCompetencies) + { +
+ + Preview + +
+ } +
+
+ + +
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyRoleRequirements.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyRoleRequirements.cshtml new file mode 100644 index 0000000000..c55108c58c --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyRoleRequirements.cshtml @@ -0,0 +1,95 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model ManageCompetencyRoleRequirementsViewModel; +@{ + ViewData["Title"] = "Manage Role Requirements"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Self assessment @Model.VocabularySingular.ToLower() role requirements

+

+ Specify whether role requirements will be enforced for the @Model.CompetencyAssessmentName self assessment. When requirements are set, they must be met by the learner before the self assessment can be signed off. +

+
+
+
+ Enforce role requirements for self assessment +
+
+ @if (Model.EnforceRoleRequirementsForSignOff) + { + Yes. Enforce role requirements. + } + else + { + No. Do not enforce role requirements. + } +
+
+ Change role requirements enforcement +
+
+
+
+ Include requirement status filters +
+
+ @if (Model.IncludeRequirementsFilters) + { + Yes. Include filters. + } + else + { + No. Do not include filters. + } +
+
+ Change include requirements filters +
+
+
+
+ @Model.VocabularySingular role requirements +
+
+ @Model.CountCompetencyRequirements @Model.VocabularySingular.ToLower() assessment questions with role requirements set. +
+
+ Change include requirements filters +
+
+
+
+ + + + + + + +
+ diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageOptionalCompetencies.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageOptionalCompetencies.cshtml new file mode 100644 index 0000000000..63b7445355 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageOptionalCompetencies.cshtml @@ -0,0 +1,106 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model SelectOptionalCompetenciesViewModel; +@{ + ViewData["Title"] = "Manage Optional Competencies"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Manage optional @Model.VocabularyPlural.ToLower()

+

+ Manage optional @Model.VocabularyPlural.ToLower() for the assessment @Model.CompetencyAssessmentName. + Optional @Model.VocabularyPlural.ToLower() can be selected for inclusion in a self assessment by a learner if required. +

+
+
+
+ Optional @Model.VocabularyPlural.ToLower() +
+
+ @if (!Model.SelectedCompetencyIds.Any()) + { + No @Model.VocabularyPlural.ToLower() have been made optional for this self assessment. + } + else + { + + @(Model.CompetencyGroups.Count(g => + g.Any(c => c.GroupOptionalCompetencies) + )) optional @Model.VocabularySingular.ToLower() groups +
+ @(Model.CompetencyGroups + .Where(g => g.All(c => !c.GroupOptionalCompetencies)) + .Sum(g => g.Count())) individual optional @Model.VocabularyPlural.ToLower() from + @(Model.CompetencyGroups.Count(g => + g.All(c => !c.GroupOptionalCompetencies) + )) groups +
+ + } +
+
+ Change optional @Model.VocabularyPlural.ToLower() +
+
+ @if (Model.SelectedCompetencyIds.Any()) + { +
+
+ Minimum optional @Model.VocabularyPlural.ToLower() +
+
+ @(Model.MinimumOptionalCompetencies == null | Model.MinimumOptionalCompetencies == 0 ? "No minimum set" : Model.MinimumOptionalCompetencies.ToString()) +
+
+ Change minimum optional @Model.VocabularyPlural.ToLower() +
+
+
+
+ Optional @Model.VocabularySingular.ToLower() learner prompt +
+
+ @Html.Raw(Model.ManageOptionalCompetenciesPrompt == null ? "No learner prompt specified" : Model.ManageOptionalCompetenciesPrompt.ToString()) +
+
+ Change learner prompt +
+
+ } +
+
+ + + + + + + + + +
+ diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageSupervisionSettings.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageSupervisionSettings.cshtml new file mode 100644 index 0000000000..92869acbf0 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageSupervisionSettings.cshtml @@ -0,0 +1,159 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model ManagesupervisionViewModel +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid && ViewData.ModelState.ContainsKey(nameof(Model.TaskCompleteChecked)); + ViewData["Title"] = "Manage supervision sessions"; +} +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} + +
+
+ @if (errorHasOccurred) + { + + } +

Manage supervision settings

+
+
+
+
+
+
+ + +
+
+ Supervisor signs off self assessment +
+
+ @Model.Signoff.SignoffText +
+
+ +
+
+ @if (@Model.Signoff.SignoffText == "Yes") + { +
+
+ Supervisor sign off statement +
+
+ @Model.SupervisorDeclaration.Declaration + + +
+
+ + + View declaration text + + +
+ @Html.Raw(@Model.SupervisorDeclaration.DeclarationText) +
+
+
+
+
+ +
+
+
+
+ Learner sign off statement +
+
+ @Model.LearnerDeclaration.Declaration + + +
+
+ + + View declaration text + + +
+ @Html.Raw(@Model.LearnerDeclaration.DeclarationText) +
+ +
+
+
+
+ +
+
+
+
+ Supervisor or nominated supervisor confirms individual assessment results +
+
+ @Model.Signoff.ConfirmText +
+ +
+ } + +
+ + + + + + + + +
+ + + + + Cancel + +
+
+
+
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Name.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Name.cshtml new file mode 100644 index 0000000000..69feaf3825 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Name.cshtml @@ -0,0 +1,109 @@ +@using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +@model CompetencyAssessmentBase; +@{ + ViewData["Title"] = "New Competency Assessments"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + @if ((string)ViewContext.RouteData.Values["actionname"] == "New") +{ + @section NavBreadcrumbs { + + } +} +else +{ + @section NavBreadcrumbs { + + } +} +@if ((string)ViewContext.RouteData.Values["actionName"] == "New") +{ +

Create a new competency assessment

+} +else +{ +

Edit competency assessment name

+} +
+ @if (!ViewData.ModelState.IsValid) + { + + } + + +
+ Choose a title that identifies scope of capabilities covered and/or the roles or levels the assessment targets. +
+ + +
+ + + + + + + + + + + + + + @if ((string)ViewContext.RouteData.Values["actionName"] == "New") + { + + + } + else + { + @if (Model.PublishStatusID == 3) + { + + } + + + } + + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/PublishReview.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/PublishReview.cshtml new file mode 100644 index 0000000000..4c8edd4950 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/PublishReview.cshtml @@ -0,0 +1,124 @@ +@using DigitalLearningSolutions.Data.Utilities +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments; +@inject IClockUtility ClockUtility +@model PublishReviewViewModel; +@{ + ViewData["Title"] = "Publish review"; +} + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} +

Publish @Model.CompetencyAssessmentName self assesment

+

Please ensure that all of the reviewers who are required to approve the self assesment are listed below.

+ + +@if (Model.SelfAssessmentReviews.Any()) +{ + + + + + + + + + + + + @foreach (var review in Model.SelfAssessmentReviews) + { + + + + + + + } + +
Framework review status
+ Reviewer + + Requested + + Status + + Actions +
+ Reviewer @review.UserEmail + + Requested @(review.ReviewRequested.ToShortDateString() != ClockUtility.UtcNow.ToShortDateString() ? review.ReviewRequested.ToShortDateString() : "Today") + + Status + + + Actions + + @if (review.ReviewComplete == null) + { + @if (review.ReviewRequested.ToShortDateString() != ClockUtility.UtcNow.ToShortDateString()) + { + + Send reminder + +   + } + + Remove request + + } + else if (!review.SignedOff) + { + + Request Re-review + + } +
+} +
+
+ @if (Model.CanPublish && Model.CompetencyAssessmentBase.UserRole > 1) + { +
+

+ + Ready to publish + +

+

There are no outstanding reviewers whose approval is required before this self assesment can be published.

+

Once published, this self assesment and its @Model.VocabPlural().ToLower() can be discovered by Profile Managers and included in national and local job profiles.

+

Please ensure that all of the reviewers who are required to approve the self assesment are listed above before publishing.

+
+ } + else + { +
+

+ + Approvals required + +

+

There are outstanding reviewers whose approval is required before this self assesment can be published.

+
+ } +
+
+ @if (Model.CanPublish) + { + Publish self assesment + } + Invite more reviewers +
+
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/PublishWithoutReview.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/PublishWithoutReview.cshtml new file mode 100644 index 0000000000..3a0c1ec716 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/PublishWithoutReview.cshtml @@ -0,0 +1,55 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments; +@model PublishWithoutReviewViewModel; +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["Title"] = "Publish without review"; +} +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} +

Publish without review

+ +
+
+
+ @if (errorHasOccurred) + { + + } +

By choosing to publish this self assessment without submitting for review, you confirm that a suitable and robust review process has taken place offline and that the self assessment has been confirmed as ready for production use with learners.

+ + + + + + + + +
+
+ +
+ diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/RemoveCompetencyGroupConfirm.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/RemoveCompetencyGroupConfirm.cshtml new file mode 100644 index 0000000000..7a0375c1b8 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/RemoveCompetencyGroupConfirm.cshtml @@ -0,0 +1,55 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@using Microsoft.Extensions.Configuration +@model CompetencyGroupDeleteViewModel +@inject IConfiguration Configuration + +@{ + ViewData["Title"] = "Competency Group - Confirm remove"; + ViewData["Application"] = "Competency Assessment Service"; + ViewData["HeaderPathName"] = "Competency Assessment Service"; +} + +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} +
+
+
+
+

+ Remove @Model.VocabularySingular Group and @Model.CompetencyCount @(Model.CompetencyCount > 1 ? Model.VocabularyPlural : Model.VocabularySingular) +

+

+ This action will remove the @Model.VocabularySingular.ToLower() group and the @Model.CompetencyCount @(Model.CompetencyCount > 1 ? Model.VocabularyPlural.ToLower() : Model.VocabularySingular.ToLower()) it contains. +

+

+ Are you sure that you wish to proceed? +

+
+ +
+
+ +
diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectFrameworkSources.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectFrameworkSources.cshtml new file mode 100644 index 0000000000..92c65a368d --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectFrameworkSources.cshtml @@ -0,0 +1,151 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model SelectFrameworkSourcesViewModel; +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["Title"] = "Select Framework Sources"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} + + +
+
+ @if (errorHasOccurred) + { + + } + +
+
+

Select framework sources for @Model.CompetencyAssessmentName

+
+ @if (Model.PrimaryFramework != null) + { +
+
+ Primary framework +
+
+ @Model.PrimaryFramework.FrameworkName +
+
+ +
+
+ } + @if (Model.AdditionalFrameworks.Count() > 0) + { + for (int i = 0; i < Model.AdditionalFrameworks.Count(); i++) + { +
+
+ Additional framework @(i+1) +
+
+ @Model.AdditionalFrameworks.ElementAt(i).FrameworkName +
+
+ +
+
+ } + } +
+@if (Model.ActionName == "AddFramework") +{ +
+
+
+ +

Add a framework source

+
+
+ @foreach (var framework in Model.Frameworks) + { +
+ + +
+ } +
+
+
+ + + +
+} +@if (Model.ActionName == "Summary") +{ + @if (Model.Frameworks.Any()) + { + + Add framework + + } +
+ + + + + + + +
+} + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectOptionalCompetencies.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectOptionalCompetencies.cshtml new file mode 100644 index 0000000000..b196b35676 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectOptionalCompetencies.cshtml @@ -0,0 +1,93 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model SelectOptionalCompetenciesViewModel; +@{ + ViewData["Title"] = "Select Optional Competencies"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Select optional competencies

+ +

+ Optional competencies can be added to the assessment individually or as a group. +

+
+
+ +

+ Which @Model.VocabularySingular.ToLower() groups and/or @Model.VocabularyPlural.ToLower() should be optional? +

+
+ @foreach (var competencyGroup in Model.CompetencyGroups) + { + @if (competencyGroup.Count() > 1) + { + +
+

+ @competencyGroup.Key +

+
+
+ + +
+ @foreach (var competency in competencyGroup) + { +
+ + +
+ } +
+
+ } + + } +
+ + + + +
+ + +@section scripts { + +} diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SendForReview.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SendForReview.cshtml new file mode 100644 index 0000000000..e9b37b124d --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SendForReview.cshtml @@ -0,0 +1,113 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments; +@model SendForReviewViewModel; +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["Title"] = "Send for review"; +} +@section NavMenuItems { + +} +@section NavBreadcrumbs { + +} +

Send for review

+

Who should review this self assessment?

+@if (errorHasOccurred) +{ + +} +
+ + + + + + + + + + + + + @foreach (var collaborator in Model.Collaborators) + { + + + + + + } + +
+ Select working group members who need to review the self assessment "@Model.CompetencyAssessmentName" before it is published. + If sign-off is required form a user before the self assessment is published, ensure that sign-off required column is checked. +
+ User + + Role + + Sign-off Required +
+ + User +
+ + +
+
+ Role + + + + + Sign-off Required +
+ + +
+
+ + + + + + +
+ + + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SetMinimumOptionalCompetencies.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SetMinimumOptionalCompetencies.cshtml new file mode 100644 index 0000000000..e0c652122a --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SetMinimumOptionalCompetencies.cshtml @@ -0,0 +1,54 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model SetMinimumOptionalCompetenciesViewModel; +@{ + ViewData["Title"] = "Set Minimum Optional Competencies"; + ViewData["Application"] = "Framework Service"; +} + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Set a minimum number of optional @Model.VocabularyPlural.ToLower()

+ +

+ If required, you can specify a minimum number of optional @Model.VocabularyPlural.ToLower() that a learner must select when completing their @Model.CompetencyAssessmentName self assessment. +

+
+ + +
+ Specify a number between 0 and @Model.OptionalCompetenciesCount. +
+ + +
+ + + +
+ + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SetOptionalCompetencyLearnerPrompt.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SetOptionalCompetencyLearnerPrompt.cshtml new file mode 100644 index 0000000000..2933477dc8 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SetOptionalCompetencyLearnerPrompt.cshtml @@ -0,0 +1,58 @@ +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model SetOptionalCompetencyLearnerPromptViewModel; +@{ + ViewData["Title"] = "Set Optional Competencies Learner Prompt"; + ViewData["Application"] = "Framework Service"; +} + + +@section NavMenuItems { + +} + +@section NavBreadcrumbs { + +} +

Optional @Model.VocabularyPlural.ToLower() learner prompt

+ +

+ Provide an optional prompt to help learners choose the right optional @Model.VocabularyPlural.ToLower() to include in their @Model.CompetencyAssessmentName self assessment. +

+
+ + + + + + +
+ + +@section scripts { + +} diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_ApprovalTag.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_ApprovalTag.cshtml new file mode 100644 index 0000000000..81aec8be4f --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_ApprovalTag.cshtml @@ -0,0 +1,50 @@ +@using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +@model SelfAssessmentReview +@{ + string? approvalText; + approvalText = Model.SignOffRequired ? "Required" : "Optional"; + approvalText += Model.ReviewComplete == null ? " - Pending" : Model.SignedOff ? " - Approved" : " - Rejected"; + switch (approvalText) + { + case "Required - Approved": + case "Optional - Approved": + // Use the text block below to separate html elements from code + + + @approvalText + + + break; // Always break each case + case "Optional - Pending": + + + @approvalText + + + + break; + case "Required - Pending": + + + @approvalText + + + + break; + case "Required - Rejected": + + + @approvalText + + + + break; + case "Optional - Rejected": + + + @approvalText + + + break; + } +} diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_AssessmentsGrid.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_AssessmentsGrid.cshtml new file mode 100644 index 0000000000..c9c5deba92 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_AssessmentsGrid.cshtml @@ -0,0 +1,85 @@ +@using DigitalLearningSolutions.Data.Models.CompetencyAssessments; +@model IEnumerable; + + + + + + + + + + + + + + @foreach (var competencyAssessment in Model) + { + + + + + + + + + } + +
+ Frameworks +
+ Competency Assessment + + Framework Links + + Created + + Status + + Owner + + Actions +
+ Competency Assessment + + @competencyAssessment.Brand + + / + + @competencyAssessment.CompetencyAssessmentName + + + Framework links + + @competencyAssessment.LinkedFrameworks + + + Created + + @competencyAssessment.CreatedDate.ToShortDateString() + + + Status + + + + + Owner + + @competencyAssessment.Owner + + + Actions + @if (competencyAssessment.UserRole > 1) + { + + Manage + + } + else + { + + View + + } +
diff --git a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_RoleProfilesSearchAndSort.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_CompetencyAssessmentsSearchAndSort.cshtml similarity index 66% rename from DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_RoleProfilesSearchAndSort.cshtml rename to DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_CompetencyAssessmentsSearchAndSort.cshtml index d6aa3c18d1..c3e030727f 100644 --- a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_RoleProfilesSearchAndSort.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_CompetencyAssessmentsSearchAndSort.cshtml @@ -1,11 +1,11 @@ -@using DigitalLearningSolutions.Web.ViewModels.RoleProfiles -@model BaseRoleProfilesPageViewModel - -
-
- -
-
- -
-
+@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model BaseCompetencyAssessmentsPageViewModel + +
+
+ +
+
+ +
+
diff --git a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_PageSearch.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_PageSearch.cshtml similarity index 87% rename from DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_PageSearch.cshtml rename to DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_PageSearch.cshtml index 6edb778960..ab254994c8 100644 --- a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_PageSearch.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_PageSearch.cshtml @@ -1,13 +1,13 @@ -@using DigitalLearningSolutions.Web.ViewModels.RoleProfiles -@model BaseRoleProfilesPageViewModel - - +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model BaseCompetencyAssessmentsPageViewModel + + diff --git a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_PageSort.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_PageSort.cshtml similarity index 80% rename from DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_PageSort.cshtml rename to DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_PageSort.cshtml index a8b403f8d2..a13027eeb5 100644 --- a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_PageSort.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_PageSort.cshtml @@ -1,26 +1,26 @@ -@using DigitalLearningSolutions.Web.ViewModels.RoleProfiles -@model BaseRoleProfilesPageViewModel - - -
- - -
- - - -
-
+@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model BaseCompetencyAssessmentsPageViewModel + + +
+ + +
+ + + +
+
diff --git a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_Pagination.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_Pagination.cshtml similarity index 94% rename from DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_Pagination.cshtml rename to DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_Pagination.cshtml index 3f43b6aee8..012e103877 100644 --- a/DigitalLearningSolutions.Web/Views/RoleProfiles/Shared/_Pagination.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/Shared/_Pagination.cshtml @@ -1,5 +1,5 @@ -@using DigitalLearningSolutions.Web.ViewModels.RoleProfiles -@model BaseRoleProfilesPageViewModel +@using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments +@model BaseCompetencyAssessmentsPageViewModel