From 71f87e8ed4fccedcc9bfd1c5664d08af990ec763 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Wed, 28 Dec 2022 20:27:59 -0500 Subject: [PATCH] Add priv policy; WIP on log on --- src/JobsJobsJobs/Server/Handlers.fs | 51 +- .../Server/JobsJobsJobs.Server.fsproj | 2 + src/JobsJobsJobs/Server/ViewModels.fs | 14 + src/JobsJobsJobs/Server/Views/Citizen.fs | 60 +++ src/JobsJobsJobs/Server/Views/Home.fs | 490 ++++++++++++++++++ src/JobsJobsJobs/Server/Views/Layout.fs | 47 +- 6 files changed, 641 insertions(+), 23 deletions(-) create mode 100644 src/JobsJobsJobs/Server/ViewModels.fs create mode 100644 src/JobsJobsJobs/Server/Views/Citizen.fs diff --git a/src/JobsJobsJobs/Server/Handlers.fs b/src/JobsJobsJobs/Server/Handlers.fs index 6d88e6c..1ddc2ca 100644 --- a/src/JobsJobsJobs/Server/Handlers.fs +++ b/src/JobsJobsJobs/Server/Handlers.fs @@ -4,6 +4,7 @@ module JobsJobsJobs.Api.Handlers open Giraffe open JobsJobsJobs.Domain open JobsJobsJobs.Domain.SharedTypes +open JobsJobsJobs.Views open Microsoft.AspNetCore.Http open Microsoft.Extensions.Logging @@ -56,6 +57,7 @@ open NodaTime module Helpers = open System.Security.Claims + open Giraffe.Htmx open Microsoft.Extensions.Configuration open Microsoft.Extensions.Options @@ -95,6 +97,20 @@ module Helpers = /// Return an empty OK response let ok : HttpHandler = Successful.OK "" + // -- NEW -- + + /// Render a page-level view + let render pageTitle content : HttpHandler = fun _ ctx -> task { + let renderFunc = if ctx.Request.IsHtmx && not ctx.Request.IsHtmxRefresh then Layout.partial else Layout.full + let renderCtx : Layout.PageRenderContext = { + IsLoggedOn = Option.isSome (tryUser ctx) + CurrentUrl = ctx.Request.Path.Value + PageTitle = pageTitle + Content = content + } + return! ctx.WriteHtmlViewAsync (renderFunc renderCtx) + } + open System open JobsJobsJobs.Data @@ -221,6 +237,27 @@ module Continent = } +/// Handlers for the home page, legal stuff, and help +[] +module Home = + + // GET: / + let home : HttpHandler = + render "Welcome" Home.home + + // GET: /how-it-works + let howItWorks : HttpHandler = + render "How It Works" Home.howItWorks + + // GET: /privacy-policy + let privacyPolicy : HttpHandler = + render "Privacy Policy" Home.privacyPolicy + + // GET: /terms-of-service + let termsOfService : HttpHandler = + render "Terms of Service" Home.termsOfService + + /// Handlers for /api/listing[s] routes [] module Listing = @@ -467,21 +504,15 @@ module Success = | None -> return! Error.notFound next ctx } -[] -module Home = - open JobsJobsJobs.Views - open JobsJobsJobs.Views.Layout - - let home : HttpHandler = fun next ctx -> task { - let render = { IsLoggedOn = false; PageTitle = "Welcome"; CurrentUrl = "/"; Content = Home.home } - return! htmlView (view render) next ctx - } open Giraffe.EndpointRouting /// All available endpoints for the application let allEndpoints = [ - route "/" Home.home + GET_HEAD [ route "/" Home.home ] + GET_HEAD [ route "/how-it-works" Home.howItWorks ] + GET_HEAD [ route "/privacy-policy" Home.privacyPolicy ] + GET_HEAD [ route "/terms-of-service" Home.termsOfService ] subRoute "/api" [ subRoute "/citizen" [ GET_HEAD [ routef "/%O" Citizen.get ] diff --git a/src/JobsJobsJobs/Server/JobsJobsJobs.Server.fsproj b/src/JobsJobsJobs/Server/JobsJobsJobs.Server.fsproj index 813f28e..b9859c9 100644 --- a/src/JobsJobsJobs/Server/JobsJobsJobs.Server.fsproj +++ b/src/JobsJobsJobs/Server/JobsJobsJobs.Server.fsproj @@ -10,8 +10,10 @@ + + diff --git a/src/JobsJobsJobs/Server/ViewModels.fs b/src/JobsJobsJobs/Server/ViewModels.fs new file mode 100644 index 0000000..439358a --- /dev/null +++ b/src/JobsJobsJobs/Server/ViewModels.fs @@ -0,0 +1,14 @@ +/// View models for Jobs, Jobs, Jobs +module JobsJobsJobs.ViewModels + +/// View model for the log on page +type LogOnViewModel = + { /// A message regarding an error encountered during a log on attempt + ErrorMessage : string option + + /// The e-mail address for the user attempting to log on + Email : string + + /// The password of the user attempting to log on + Password : string + } diff --git a/src/JobsJobsJobs/Server/Views/Citizen.fs b/src/JobsJobsJobs/Server/Views/Citizen.fs new file mode 100644 index 0000000..645f696 --- /dev/null +++ b/src/JobsJobsJobs/Server/Views/Citizen.fs @@ -0,0 +1,60 @@ +/// Views for URLs beginning with /citizen +[] +module JobsJobsJobs.Views.Citizen + +open Giraffe.ViewEngine +open JobsJobsJobs.ViewModels + +/// The log on page +let logOn (m : LogOnViewModel) = + article [] [ + h3 [ _class "pb-3" ] [ rawText "Log On" ] + match m.ErrorMessage with + | Some msg -> + p [ _class "pb-3 text-center" ] [ + span [ _class "text-danger" ] [ str msg ]; br [] + if msg.IndexOf("ocked") > -1 then + rawText "If this is a new account, it must be confirmed before it can be used; otherwise, you need " + rawText "to " + a [ _href "/citizen/forgot-password" ] [ rawText "request an unlock code" ] + rawText " before you may log on." + ] + | None -> () + form [ _class "row g-3 pb-3"] [ + div [ _class "col-12 col-md-6" ] [ + div [ _class "form-floating" ] [ + input [ _type "email" + _id "email" + _class "form-control" + _name (nameof m.Email) + _placeholder "E-mail Address" + _value m.Email + _required + _autofocus ] + label [ _class "jjj-required"; _for "email" ] [ rawText "E-mail Address" ] + ] + ] + div [ _class "col-12 col-md-6" ] [ + div [ _class "form-floating" ] [ + input [ _type "password" + _id "password" + _class "form-control" + _name (nameof m.Password) + _placeholder "Password" + _required ] + label [ _class "jjj-required"; _for "password" ] [ rawText "Password" ] + ] + ] + div [ _class "col-12" ] [ + button [ _class "btn btn-primary"; _type "submit" ] [ + i [ _class "mdi mdi-login" ] []; rawText "  Log On" + ] + ] + ] + p [ _class "text-center" ] [ + rawText "Need an account? "; a [ _href "/citizen/register" ] [ rawText "Register for one!" ] + ] + p [ _class "text-center" ] [ + rawText "Forgot your password? "; a [ _href "/citizen/forgot-password" ] [ rawText "Request a reset." ] + ] + ] diff --git a/src/JobsJobsJobs/Server/Views/Home.fs b/src/JobsJobsJobs/Server/Views/Home.fs index 86df136..d832082 100644 --- a/src/JobsJobsJobs/Server/Views/Home.fs +++ b/src/JobsJobsJobs/Server/Views/Home.fs @@ -22,6 +22,496 @@ let home = ] ] +/// Online help / documentation +let howItWorks = + article [] [ + h3 [] [ rawText "How It Works" ] + p [] [ rawText "TODO: convert and update for v3"] + ] + +/// The privacy policy +let privacyPolicy = + let appName = rawText "Jobs, Jobs, Jobs" + article [] [ + h3 [] [ rawText "Privacy Policy" ] + p [ _class "fst-italic" ] [ rawText "(as of December 27th, 2022)" ] + + p [] [ + appName; rawText " (“we,” “our,” or “us”) is committed to protecting " + rawText "your privacy. This Privacy Policy explains how your personal information is collected, used, and " + rawText "disclosed by "; appName; rawText "." + ] + p [] [ + rawText "This Privacy Policy applies to our website, and its associated subdomains (collectively, our " + rawText "“Service”) alongside our application, "; appName; rawText ". By accessing or using " + rawText "our Service, you signify that you have read, understood, and agree to our collection, storage, " + rawText "use, and disclosure of your personal information as described in this Privacy Policy and our " + rawText "Terms of Service." + ] + + h4 [] [ rawText "Definitions and key terms" ] + p [] [ + rawText "To help explain things as clearly as possible in this Privacy Policy, every time any of these " + rawText "terms are referenced, are strictly defined as:" + ] + ul [] [ + li [] [ + rawText "Cookie: small amount of data generated by a website and saved by your web browser. It is used " + rawText "to identify your browser, provide analytics, remember information about you such as your " + rawText "language preference or login information." + ] + li [] [ + rawText "Company: when this policy mentions “Company,” “we,” “us,” " + rawText "or “our,” it refers to "; appName; rawText ", that is responsible for your " + rawText "information under this Privacy Policy." + ] + li [] [ + rawText "Country: where "; appName; rawText " or the owners/founders of "; appName + rawText " are based, in this case is US." + ] + li [] [ + rawText "Customer: refers to the company, organization or person that signs up to use the "; appName + rawText " Service to manage the relationships with your consumers or service users." + ] + li [] [ + rawText "Device: any internet connected device such as a phone, tablet, computer or any other device " + rawText "that can be used to visit "; appName; rawText " and use the services." + ] + li [] [ + rawText "IP address: Every device connected to the Internet is assigned a number known as an Internet " + rawText "protocol (IP) address. These numbers are usually assigned in geographic blocks. An IP address " + rawText "can often be used to identify the location from which a device is connecting to the Internet." + ] + li [] [ + rawText "Personnel: refers to those individuals who are employed by "; appName; rawText " or are under " + rawText "contract to perform a service on behalf of one of the parties." + ] + li [] [ + rawText "Personal Data: any information that directly, indirectly, or in connection with other " + rawText "information — including a personal identification number — allows for the identification or " + rawText "identifiability of a natural person." + ] + li [] [ + rawText "Service: refers to the service provided by "; appName; rawText " as described in the relative " + rawText "terms (if available) and on this platform." + ] + li [] [ + rawText "Third-party service: refers to advertisers, contest sponsors, promotional and marketing " + rawText "partners, and others who provide our content or whose products or services we think may " + rawText "interest you." + ] + li [] [ + rawText "Website: "; appName; rawText "’s site, which can be accessed via this URL: " + a [ _href "/" ] [ rawText "https://noagendacareers.com/" ] + ] + li [] [ + rawText "You: a person or entity that is registered with "; appName; rawText " to use the Services." + ] + ] + + h4 [] [ rawText "What Information Do We Collect?" ] + p [] [ + rawText "We collect information from you when you visit our website, register on our site, or fill out a " + rawText "form." + ] + ul [] [ + li [] [ rawText "Name / Username" ] + li [] [ rawText "Coarse Geographic Location" ] + li [] [ rawText "Employment History" ] + li [] [ rawText "Job Listing Information" ] + ] + + h4 [] [ rawText "How Do We Use The Information We Collect?" ] + p [] [ rawText "Any of the information we collect from you may be used in one of the following ways:" ] + ul[] [ + li [] [ + rawText "To personalize your experience (your information helps us to better respond to your " + rawText "individual needs)" + ] + li [] [ + rawText "To improve our website (we continually strive to improve our website offerings based on the " + rawText "information and feedback we receive from you)" + ] + li [] [ + rawText "To improve customer service (your information helps us to more effectively respond to your " + rawText "customer service requests and support needs)" + ] + ] + + h4 [] [ rawText "When does "; appName; rawText " use end user information from third parties?" ] + p [] [ + appName; rawText " will collect End User Data necessary to provide the "; appName + rawText " services to our customers." + ] + p [] [ + rawText "End users may voluntarily provide us with information they have made available on social media " + rawText "websites. If you provide us with any such information, we may collect publicly available " + rawText "information from the social media websites you have indicated. You can control how much of your " + rawText "information social media websites make public by visiting these websites and changing your " + rawText "privacy settings." + ] + + h4 [] [ rawText "When does "; appName; rawText " use customer information from third parties?" ] + p [] [ rawText "We do not utilize third party information apart from the end-user data described above." ] + + h4 [] [ rawText "Do we share the information we collect with third parties?" ] + p [] [ + rawText "We may disclose personal and non-personal information about you to government or law enforcement " + rawText "officials or private parties as we, in our sole discretion, believe necessary or appropriate in " + rawText "order to respond to claims, legal process (including subpoenas), to protect our rights and " + rawText "interests or those of a third party, the safety of the public or any person, to prevent or stop " + rawText "any illegal, unethical, or legally actionable activity, or to otherwise comply with applicable " + rawText "court orders, laws, rules and regulations." + ] + + h4 [] [ rawText "Where and when is information collected from customers and end users?" ] + p [] [ + appName; rawText " will collect personal information that you submit to us. We may also receive personal " + rawText "information about you from third parties as described above." + ] + + h4 [] [ rawText "How Do We Use Your E-mail Address?" ] + p [] [ + appName; rawText " uses your e-mail address to identify you, along with your password, as an authorized " + rawText "user of this site. E-mail addresses are verified via a time-sensitive link, and may also be used " + rawText "to send password reset authorization codes. We do not display this e-mail address to users. If " + rawText "you choose to add an e-mail address as a contact type, that e-mail address will be visible to " + rawText "other authorized users." + ] + + h4 [] [ rawText "How Long Do We Keep Your Information?" ] + p [] [ + rawText "We keep your information only so long as we need it to provide "; appName; rawText " to you and " + rawText "fulfill the purposes described in this policy. When we no longer need to use your information and " + rawText "there is no need for us to keep it to comply with our legal or regulatory obligations, we’ll " + rawText "either remove it from our systems or depersonalize it so that we can’t identify you." + ] + + h4 [] [ rawText "How Do We Protect Your Information?" ] + p [] [ + rawText "We implement a variety of security measures to maintain the safety of your personal information " + rawText "when you enter, submit, or access your personal information. We mandate the use of a secure " + rawText "server. We cannot, however, ensure or warrant the absolute security of any information you " + rawText "transmit to "; appName; rawText " or guarantee that your information on the Service may not be " + rawText "accessed, disclosed, altered, or destroyed by a breach of any of our physical, technical, or " + rawText "managerial safeguards." + ] + + h4 [] [ rawText "Could my information be transferred to other countries?" ] + p [] [ + appName; rawText " is hosted in the US. Information collected via our website may be viewed and hosted " + rawText "anywhere in the world, including countries that may not have laws of general applicability " + rawText "regulating the use and transfer of such data. To the fullest extent allowed by applicable law, by " + rawText "using any of the above, you voluntarily consent to the trans-border transfer and hosting of such " + rawText "information." + ] + + h4 [] [ rawText "Is the information collected through the "; appName; rawText " Service secure?" ] + p [] [ + rawText "We take precautions to protect the security of your information. We have physical, electronic, " + rawText "and managerial procedures to help safeguard, prevent unauthorized access, maintain data security, " + rawText "and correctly use your information. However, neither people nor security systems are foolproof, " + rawText "including encryption systems. In addition, people can commit intentional crimes, make mistakes, " + rawText "or fail to follow policies. Therefore, while we use reasonable efforts to protect your personal " + rawText "information, we cannot guarantee its absolute security. If applicable law imposes any " + rawText "non-disclaimable duty to protect your personal information, you agree that intentional misconduct " + rawText "will be the standards used to measure our compliance with that duty." + ] + + h4 [] [ rawText "Can I update or correct my information?" ] + p [] [ + rawText "The rights you have to request updates or corrections to the information "; appName + rawText " collects depend on your relationship with "; appName; rawText "." + ] + p [] [ + rawText "Customers have the right to request the restriction of certain uses and disclosures of personally " + rawText "identifiable information as follows. You can contact us in order to (1) update or correct your " + rawText "personally identifiable information, or (3) delete the personally identifiable information " + rawText "maintained about you on our systems (subject to the following paragraph), by cancelling your " + rawText "account. Such updates, corrections, changes and deletions will have no effect on other " + rawText "information that we maintain in accordance with this Privacy Policy prior to such update, " + rawText "correction, change, or deletion. You are responsible for maintaining the secrecy of your unique " + rawText "password and account information at all times." + ] + p [] [ + appName; rawText " also provides ways for users to modify or remove the information we have collected from " + rawText "them from the application; these actions will have the same effect as contacting us to modify or " + rawText "remove data." + ] + p [] [ + rawText "You should be aware that it is not technologically possible to remove each and every record of " + rawText "the information you have provided to us from our system. The need to back up our systems to " + rawText "protect information from inadvertent loss means that a copy of your information may exist in a " + rawText "non-erasable form that will be difficult or impossible for us to locate. Promptly after receiving " + rawText "your request, all personal information stored in databases we actively use, and other readily " + rawText "searchable media will be updated, corrected, changed, or deleted, as appropriate, as soon as and " + rawText "to the extent reasonably and technically practicable." + ] + p [] [ + rawText "If you are an end user and wish to update, delete, or receive any information we have about you, " + rawText "you may do so by contacting the organization of which you are a customer." + ] + + h4 [] [ rawText "Governing Law" ] + p [] [ + rawText "This Privacy Policy is governed by the laws of US without regard to its conflict of laws " + rawText "provision. You consent to the exclusive jurisdiction of the courts in connection with any action " + rawText "or dispute arising between the parties under or in connection with this Privacy Policy except for " + rawText "those individuals who may have rights to make claims under Privacy Shield, or the Swiss-US " + rawText "framework." + ] + p [] [ + rawText "The laws of US, excluding its conflicts of law rules, shall govern this Agreement and your use of " + rawText "the website. Your use of the website may also be subject to other local, state, national, or " + rawText "international laws." + ] + p [] [ + rawText "By using "; appName; rawText " or contacting us directly, you signify your acceptance of this " + rawText "Privacy Policy. If you do not agree to this Privacy Policy, you should not engage with our " + rawText "website, or use our services. Continued use of the website, direct engagement with us, or " + rawText "following the posting of changes to this Privacy Policy that do not significantly affect the use " + rawText "or disclosure of your personal information will mean that you accept those changes." + ] + + h4 [] [ rawText "Your Consent" ] + p [] [ + rawText "We’ve updated our Privacy Policy to provide you with complete transparency into what is " + rawText "being set when you visit our site and how it’s being used. By using our website, " + rawText "registering an account, or making a purchase, you hereby consent to our Privacy Policy and agree " + rawText "to its terms." + ] + + h4 [] [ rawText "Links to Other Websites" ] + p [] [ + rawText "This Privacy Policy applies only to the Services. The Services may contain links to other " + rawText "websites not operated or controlled by "; appName; rawText ". We are not responsible for the " + rawText "content, accuracy or opinions expressed in such websites, and such websites are not investigated, " + rawText "monitored or checked for accuracy or completeness by us. Please remember that when you use a link " + rawText "to go from the Services to another website, our Privacy Policy is no longer in effect. Your " + rawText "browsing and interaction on any other website, including those that have a link on our platform, " + rawText "is subject to that website’s own rules and policies. Such third parties may use their own " + rawText "cookies or other methods to collect information about you." + ] + + h4 [] [ rawText "Cookies" ] + p [] [ + appName; rawText " uses a session Cookie to identify an active, logged-on session. This Cookie is removed " + rawText "when You explicitly log off; is not accessible via script; and must be transferred over a " + rawText "secured, encrypted connection." + ] + p [] [ + appName; rawText " uses no persistent or Third-Party Cookies." + ] + + h4 [] [ rawText "Kids’ Privacy" ] + p [] [ + rawText "We do not address anyone under the age of 13. We do not knowingly collect personally identifiable " + rawText "information from anyone under the age of 13. If You are a parent or guardian and You are aware " + rawText "that Your child has provided Us with Personal Data, please contact Us. If We become aware that We " + rawText "have collected Personal Data from anyone under the age of 13 without verification of parental " + rawText "consent, We take steps to remove that information from Our servers." + ] + + h4 [] [ rawText "Changes To Our Privacy Policy" ] + p [] [ + rawText "We may change our Service and policies, and we may need to make changes to this Privacy Policy so " + rawText "that they accurately reflect our Service and policies. Unless otherwise required by law, we will " + rawText "notify you (for example, through our Service) before we make changes to this Privacy Policy and " + rawText "give you an opportunity to review them before they go into effect. Then, if you continue to use " + rawText "the Service, you will be bound by the updated Privacy Policy. If you do not want to agree to this " + rawText "or any updated Privacy Policy, you can delete your account." + ] + + h4 [] [ rawText "Third-Party Services" ] + p [] [ + rawText "We may display, include or make available third-party content (including data, information, " + rawText "applications and other products services) or provide links to third-party websites or services " + rawText "(“Third-Party Services”)." + ] + p [] [ + rawText "You acknowledge and agree that "; appName; rawText " shall not be responsible for any Third-Party " + rawText "Services, including their accuracy, completeness, timeliness, validity, copyright compliance, " + rawText "legality, decency, quality or any other aspect thereof. "; appName; rawText " does not assume and " + rawText "shall not have any liability or responsibility to you or any other person or entity for any " + rawText "Third-Party Services." + ] + p [] [ + rawText "Third-Party Services and links thereto are provided solely as a convenience to you and you access " + rawText "and use them entirely at your own risk and subject to such third parties’ terms and " + rawText "conditions." + ] + + h4 [] [ rawText "Tracking Technologies" ] + p [] [ appName; rawText " does not use any tracking technologies." ] + + h4 [] [ rawText "Information about General Data Protection Regulation (GDPR)" ] + p [] [ + rawText "We may be collecting and using information from you if you are from the European Economic Area " + rawText "(EEA), and in this section of our Privacy Policy we are going to explain exactly how and why is " + rawText "this data collected, and how we maintain this data under protection from being replicated or used " + rawText "in the wrong way." + ] + + h5 [] [ rawText "What is GDPR?" ] + p [] [ + rawText "GDPR is an EU-wide privacy and data protection law that regulates how EU residents’ data is " + rawText "protected by companies and enhances the control the EU residents have, over their personal data." + ] + p [] [ + rawText "The GDPR is relevant to any globally operating company and not just the EU-based businesses and " + rawText "EU residents. Our customers’ data is important irrespective of where they are located, which is " + rawText "why we have implemented GDPR controls as our baseline standard for all our operations worldwide." + ] + + h5 [] [ rawText "What is personal data?" ] + p [] [ + rawText "Any data that relates to an identifiable or identified individual. GDPR covers a broad spectrum " + rawText "of information that could be used on its own, or in combination with other pieces of information, " + rawText "to identify a person. Personal data extends beyond a person’s name or email address. Some " + rawText "examples include financial information, political opinions, genetic data, biometric data, IP " + rawText "addresses, physical address, sexual orientation, and ethnicity." + ] + p [] [ rawText "The Data Protection Principles include requirements such as:" ] + ul [] [ + li [] [ + rawText "Personal data collected must be processed in a fair, legal, and transparent way and should " + rawText "only be used in a way that a person would reasonably expect." + ] + li [] [ + rawText "Personal data should only be collected to fulfil a specific purpose and it should only be " + rawText "used for that purpose. Organizations must specify why they need the personal data when they " + rawText "collect it." + ] + li [] [ rawText "Personal data should be held no longer than necessary to fulfil its purpose." ] + li [] [ + rawText "People covered by the GDPR have the right to access their own personal data. They can also " + rawText "request a copy of their data, and that their data be updated, deleted, restricted, or moved " + rawText "to another organization." + ] + ] + + h5 [] [ rawText "Why is GDPR important?" ] + p [] [ + rawText "GDPR adds some new requirements regarding how companies should protect individuals’ " + rawText "personal data that they collect and process. It also raises the stakes for compliance by " + rawText "increasing enforcement and imposing greater fines for breach. Beyond these facts, it’s " + rawText "simply the right thing to do. At "; appName; rawText " we strongly believe that your data privacy " + rawText "is very important and we already have solid security and privacy practices in place that go " + rawText "beyond the requirements of this regulation." + ] + + h5 [] [ rawText "Individual Data Subject’s Rights - Data Access, Portability, and Deletion" ] + p [] [ + rawText "We are committed to helping our customers meet the data subject rights requirements of GDPR. " + appName; rawText " processes or stores all personal data in fully vetted, DPA compliant vendors. We do " + rawText "store all conversation and personal data for up to 6 years unless your account is deleted. In " + rawText "which case, we dispose of all data in accordance with our Terms of Service and Privacy Policy, " + rawText "but we will not hold it longer than 60 days." + ] + p [] [ + rawText "We are aware that if you are working with EU customers, you need to be able to provide them with " + rawText "the ability to access, update, retrieve and remove personal data. We got you! We’ve been " + rawText "set up as self service from the start and have always given you access to your data. Our customer " + rawText "support team is here for you to answer any questions you might have about working with the API." + ] + + h4 [] [ rawText "California Residents" ] + p [] [ + rawText "The California Consumer Privacy Act (CCPA) requires us to disclose categories of Personal " + rawText "Information we collect and how we use it, the categories of sources from whom we collect Personal " + rawText "Information, and the third parties with whom we share it, which we have explained above." + ] + p [] [ + rawText "We are also required to communicate information about rights California residents have under " + rawText "California law. You may exercise the following rights:" + ] + ul [] [ + li [] [ + rawText "Right to Know and Access. You may submit a verifiable request for information regarding the: " + rawText "(1) categories of Personal Information we collect, use, or share; (2) purposes for which " + rawText "categories of Personal Information are collected or used by us; (3) categories of sources " + rawText "from which we collect Personal Information; and (4) specific pieces of Personal Information " + rawText "we have collected about you." + ] + li [] [ + rawText "Right to Equal Service. We will not discriminate against you if you exercise your privacy " + rawText "rights." + ] + li [] [ + rawText "Right to Delete. You may submit a verifiable request to close your account and we will delete " + rawText "Personal Information about you that we have collected." + ] + li [] [ + rawText "Request that a business that sells a consumer’s personal data, not sell the " + rawText "consumer’s personal data." + ] + ] + p [] [ + rawText "If you make a request, we have one month to respond to you. If you would like to exercise any of " + rawText "these rights, please contact us." + ] + p [] [ rawText "We do not sell the Personal Information of our users." ] + p [] [ rawText "For more information about these rights, please contact us." ] + + h5 [] [ rawText "California Online Privacy Protection Act (CalOPPA)" ] + p [] [ + rawText "CalOPPA requires us to disclose categories of Personal Information we collect and how we use it, " + rawText "the categories of sources from whom we collect Personal Information, and the third parties with " + rawText "whom we share it, which we have explained above." + ] + p [] [ rawText "CalOPPA users have the following rights:" ] + ul [] [ + li [] [ + rawText "Right to Know and Access. You may submit a verifiable request for information regarding the: " + rawText "(1) categories of Personal Information we collect, use, or share; (2) purposes for which " + rawText "categories of Personal Information are collected or used by us; (3) categories of sources " + rawText "from which we collect Personal Information; and (4) specific pieces of Personal Information " + rawText "we have collected about you." + ] + li [] [ + rawText "Right to Equal Service. We will not discriminate against you if you exercise your privacy " + rawText "rights." + ] + li [] [ + rawText "Right to Delete. You may submit a verifiable request to close your account and we will delete " + rawText "Personal Information about you that we have collected." + ] + li [] [ + rawText "Right to request that a business that sells a consumer’s personal data, not sell the " + rawText "consumer’s personal data." + ] + ] + p [] [ + rawText "If you make a request, we have one month to respond to you. If you would like to exercise any of " + rawText "these rights, please contact us." + ] + p [] [ rawText "We do not sell the Personal Information of our users." ] + p [] [ rawText "For more information about these rights, please contact us." ] + + h4 [] [ rawText "Contact Us" ] + p [] [ rawText "Don’t hesitate to contact us if you have any questions." ] + ul [] [ + li [] [ + rawText "Via this Link: " + a [ _href "/how-it-works" ] [rawText "https://noagendacareers.com/how-it-works" ] + ] + ] + + hr [] + + p [ _class "fst-italic" ] [ rawText "Changes for "; appName; rawText " v3 (December 27th, 2022)" ] + ul [] [ + li [ _class "fst-italic" ] [ rawText "Removed references to Mastodon" ] + li [ _class "fst-italic" ] [ rawText "Added references to job listings" ] + li [ _class "fst-italic" ] [ rawText "Changed information regarding e-mail addresses" ] + li [ _class "fst-italic" ] [ rawText "Updated cookie / tracking sections for new architecture" ] + ] + p [ _class "fst-italic" ] [ + rawText "Change on September 6th, 2021 – replaced “No Agenda Social” with " + rawText "generic terms for any authorized Mastodon instance." + ] + ] + /// The page for terms of service let termsOfService = article [] [ diff --git a/src/JobsJobsJobs/Server/Views/Layout.fs b/src/JobsJobsJobs/Server/Views/Layout.fs index d33c147..72f70b5 100644 --- a/src/JobsJobsJobs/Server/Views/Layout.fs +++ b/src/JobsJobsJobs/Server/Views/Layout.fs @@ -19,17 +19,23 @@ type PageRenderContext = Content : XmlNode } +/// Append the application name to the page title +let private constructTitle ctx = + seq { + if ctx.PageTitle <> "" then + ctx.PageTitle; " | " + "Jobs, Jobs, Jobs" + } + |> Seq.reduce (+) + |> str + |> List.singleton + |> title [] + /// Generate the HTML head tag let private htmlHead ctx = - let pageTitle = - seq { - if ctx.PageTitle <> "" then - ctx.PageTitle; " | " - "Jobs, Jobs, Jobs" - } |> Seq.reduce (+) head [] [ meta [ _name "viewport"; _content "width=device-width, initial-scale=1" ] - title [] [ str pageTitle ] + constructTitle ctx link [ _href "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" _rel "stylesheet" _integrity "sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" @@ -53,9 +59,9 @@ let private links ctx = navLink "/profile/search" "view-list-outline" "Employment Profiles" navLink "/success-story/list" "thumb-up" "Success Stories" div [ _class "separator" ] [] - navLink "/citizen/account" "mdiAccountEdit" "My Account" - navLink "/listings/mine" "mdiSignText" "My Job Listings" - navLink "/profile/edit" "mdiPencil" "My Employment Profile" + navLink "/citizen/account" "account-edit" "My Account" + navLink "/listings/mine" "sign-text" "My Job Listings" + navLink "/profile/edit" "pencil" "My Employment Profile" div [ _class "separator" ] [] navLink "/citizen/log-off" "mdiLogoutVariant" "Log Off" else @@ -125,13 +131,12 @@ let private htmlFoot = ] /// Create a full view -let view ctx = +let full ctx = html [ _lang "en" ] [ htmlHead ctx body [] [ - div [ _class "jjj-app" ] [ + div [ _class "jjj-app"; _hxBoost; _hxTarget "this" ] [ yield! sideNavs ctx - //otherSideNav ctx div [ _class "jjj-main" ] [ yield! titleBars main [ _class "jjj-content container-fluid" ] [ ctx.Content ] @@ -146,3 +151,19 @@ let view ctx = script [ _src "/script.js" ] [] ] ] + +/// Create a partial (boosted response) view +let partial ctx = + html [ _lang "en" ] [ + head [] [ + constructTitle ctx + ] + body [] [ + yield! sideNavs ctx + div [ _class "jjj-main" ] [ + yield! titleBars + main [ _class "jjj-content container-fluid" ] [ ctx.Content ] + htmlFoot + ] + ] + ]