- Forgot your password?
- {' '}
- Request a reset or
- {' '}
+ Forgot your password? Request a reset or{' '}
a magic sign in link.
- ¿Olvidó su contraseña? Solicitar
- {' '}
- un restablecimiento de contraseña or
- {' '}
+ ¿Olvidó su contraseña? Solicitar{' '}
+ un restablecimiento de contraseña or{' '}
un enlace de inicio de sesión mágico.
diff --git a/client/src/pages/users/EditOtherParentPage.tsx b/client/src/pages/users/EditOtherParentPage.tsx
new file mode 100644
index 0000000..a9728d8
--- /dev/null
+++ b/client/src/pages/users/EditOtherParentPage.tsx
@@ -0,0 +1,9 @@
+import { Page } from 'framework7-react'
+
+export default function EditOtherParentPage() {
+ return (
+
+
Edit Other Parent Page
+
+ )
+}
diff --git a/client/src/pages/users/OtherParentsPage.tsx b/client/src/pages/users/OtherParentsPage.tsx
new file mode 100644
index 0000000..1845566
--- /dev/null
+++ b/client/src/pages/users/OtherParentsPage.tsx
@@ -0,0 +1,9 @@
+import { Page } from 'framework7-react'
+
+export default function EditParentsPage() {
+ return (
+
+
+ You can select which days to receive notifications or disable notifications by visiting the notification page in settings.
+
+
Thanks for your help, and let us know if you have questions.
+
Stay safe out there,
+ The Greenlight Team
+
+
+
+
+ Hola <%= user.first_name %>,
+
+
+ <%= user.first_name %> te ha agregado a Greenlight
+ Todos los días recibirá notificaciones para enviar encuestas de síntomas en nombre de sus hijos:
+
+ Puede seleccionar qué días recibir notificaciones o deshabilitar las notificaciones visitando la página de notificaciones en la configuración.
+
+
Gracias por su ayuda y avísenos si tiene preguntas.
+
Mantente a salvo,
+ El Equipo Greenlight
+
+ HTML
+ end
+
+ def sms_template
+ # TODO: Generalize
+ Erubi::Engine.new(<<~SMS
+ You have been added to Greenlight by <%= current_user.first_name %>
+ Every day you will receive notifications to submit symptom surveys on behalf of your children:
+ <%= user.children.map(%:first_name).to_sentence %>
+ Login by resetting your password or requesting a magic sign in link.
+ SMS
+ ).src
+ end
+
+ #
+ #
+ # @param [Integer] user_id
+ def perform(user)
+ user.reset_magic_sign_in_token!
+ I18n.with_locale(user.locale) do
+ if user.email?
+ SendGridEmail.new(
+ to: user.name_with_email,
+ subject: user.invited_at.blank? ? "✨ Welcome to Greenlight! / Bienvenido a Greenlight" : '✨ REMINDER: Welcome to Greenlight! / Bienvenido a Greenlight',
+ html: eval(html_template), # rubocop:disable Security/Eval
+ text: eval(sms_template), # rubocop:disable Security/Eval
+ ).run
+ end
+ if user.mobile_number?
+ PlivoSMS.new(
+ to: user.mobile_number,
+ from: Greenlight::PHONE_NUMBER,
+ message: eval(sms_template) # rubocop:disable Security/Eval
+ ).run
+ end
+ end
+
+ if user.invited_at.blank?
+ user.invited_at = Time.zone.now
+ user.save!
+ end
+ end
+end
diff --git a/client/src/api/index.ts b/client/src/api/index.ts
index 4b1b08c..c36b7c3 100644
--- a/client/src/api/index.ts
+++ b/client/src/api/index.ts
@@ -1,9 +1,7 @@
import axios, { AxiosResponse } from 'axios'
import { getGlobal, setGlobal } from 'reactn'
-import {
- assertArray, assertNotArray, assertNotNull, assertNotUndefined, transformForAPI,
-} from 'src/helpers/util'
+import { assertArray, assertNotArray, assertNotNull, assertNotUndefined, transformForAPI } from 'src/helpers/util'
// FIXME: This shouldn't be assigned here. It should go in a provider
import Honeybadger from 'honeybadger-js'
@@ -182,7 +180,7 @@ export async function checkLocationRegistrationCode(locationId: string, registra
return result.data.result
}
-export async function registerUser(locationId: string, user: RegisteringUser & { password: string}) {
+export async function registerUser(locationId: string, user: RegisteringUser & { password: string }) {
const userWithoutBlanks = transformForAPI(user, { removeBlanks: true })
await v1.post(`/locations/${locationId}/register`, userWithoutBlanks)
const currentUser = await getCurrentUser()
@@ -232,7 +230,7 @@ export async function completeWelcomeUser(user: User): Promise {
return entity
}
-export async function createUserAndSignIn(user: Partial & { password: string}): Promise {
+export async function createUserAndSignIn(user: Partial & { password: string }): Promise {
const signInResponse = await v1.post('/users/create-and-sign-in', user)
if (env.isCordova()) {
localStorage.setItem('token', signInResponse.data.token)
@@ -413,3 +411,15 @@ export function usePagedResources(
): responseInterface, any> {
return useSWR([path, page, filter], (path, page, filter) => getPagedResources(path, page, filter))
}
+
+export async function inviteAnotherParent(params: {
+ firstName: string
+ lastName: string
+ emailOrMobile: string
+ children: Array
+}): Promise {
+ const response = await v1.post>(`/parent/invite`, transformForAPI(params))
+ const entity = transformRecordResponse(response.data)
+ assertNotArray(entity)
+ return entity
+}
diff --git a/client/src/pages/DashboardPage.tsx b/client/src/pages/DashboardPage.tsx
index d1b10cd..588cfc2 100644
--- a/client/src/pages/DashboardPage.tsx
+++ b/client/src/pages/DashboardPage.tsx
@@ -10,6 +10,7 @@ import {
NavTitle,
NavRight,
Icon,
+ Block,
} from 'framework7-react'
import { esExclaim, greeting } from 'src/helpers/util'
import If from 'src/components/If'
@@ -22,7 +23,7 @@ import { User } from 'src/models'
import ReleaseCard from 'src/components/ReleaseCard'
import { F7Props } from 'src/types'
import Redirect from 'src/components/Redirect'
-import { tr } from 'src/components/Tr'
+import Tr, { tr } from 'src/components/Tr'
import UserJDenticon from '../components/UserJDenticon'
function UserList({ users }: { users: User[] }) {
@@ -85,7 +86,8 @@ export default function DashboardPage(props: F7Props): JSX.Element {
- {esExclaim()}{greeting()}, {currentUser.firstName}!
+ {esExclaim()}
+ {greeting()}, {currentUser.firstName}!
@@ -160,47 +162,62 @@ export default function DashboardPage(props: F7Props): JSX.Element {
{/* User is a worker and has children */}
- {currentUser.hasLocationThatRequiresSurvey() && currentUser.isParent()
- && Your Family}
+ {currentUser.hasLocationThatRequiresSurvey() && currentUser.isParent() && (
+ Your Family
+ )}
{/* User is only a parent */}
- {!currentUser.hasLocationThatRequiresSurvey() && currentUser.isParent() && Your Children}
+ {!currentUser.hasLocationThatRequiresSurvey() && currentUser.isParent() && (
+ Your Children
+ )}
{/* User is not a parent */}
{!currentUser.isParent() && Your Status}
+
+
+
+
+
+
+ {currentUser.isParent() && (
+
+ )}
+ Resources For You
- {
- currentUser.isAdminSomewhere()
- && (
-
-
-
-
- {
- currentUser.adminLocations().map((location) => (
-
- ))
- }
-
-
-
- )
- }
+ {currentUser.isAdminSomewhere() && (
+
+
+
+
+ {currentUser.adminLocations().map((location) => (
+
+ ))}
+
+
+
+ )}
@@ -209,8 +226,10 @@ export default function DashboardPage(props: F7Props): JSX.Element {
link={paths.chwRequestPath}
title={tr({ en: 'Connect to Services', es: 'Conectarse a los servicios' })}
footer={tr({
- en: 'Send a request to a community health worker for help with healthcare, housing, legal services, COVID-19 supplies and more.',
- es: 'Envíe una solicitud a un trabajador de salud de la comunidad para que le ayude con la atención médica, la vivienda, los servicios legales, los suministros de COVID-19 y más.',
+ en:
+ 'Send a request to a community health worker for help with healthcare, housing, legal services, COVID-19 supplies and more.',
+ es:
+ 'Envíe una solicitud a un trabajador de salud de la comunidad para que le ayude con la atención médica, la vivienda, los servicios legales, los suministros de COVID-19 y más.',
})}
>
@@ -226,15 +245,11 @@ export default function DashboardPage(props: F7Props): JSX.Element {
@@ -250,23 +265,24 @@ export default function DashboardPage(props: F7Props): JSX.Element {
{!currentUser.isInBrevard__HACK() && (
)}
- {
- currentUser.isInBrevard__HACK() && (
-
-
-
- )
- }
+ {currentUser.isInBrevard__HACK() && (
+
+
+
+ )}
{/* https://ncchildcare.ncdhhs.gov/Portals/0/documents/pdf/P/Parent_and_Families_School_Age_Child_Care.pdf?ver=2020-08-26-122445-963 */}
@@ -315,7 +334,6 @@ export default function DashboardPage(props: F7Props): JSX.Element {
>
*/}
-
)
diff --git a/client/src/pages/users/InviteOtherParentPage.tsx b/client/src/pages/users/InviteOtherParentPage.tsx
index 617654b..9c7a12a 100644
--- a/client/src/pages/users/InviteOtherParentPage.tsx
+++ b/client/src/pages/users/InviteOtherParentPage.tsx
@@ -1,7 +1,7 @@
-import { useRef } from 'react'
+import { useRef, useCallback } from 'react'
import { FormikProvider, useFormik, yupToFormErrors } from 'formik'
-import { Block, BlockTitle, Button, List, ListItem, Navbar, Page } from 'framework7-react'
-import { store } from 'src/api'
+import { Block, BlockTitle, Button, f7, List, ListItem, Navbar, Page } from 'framework7-react'
+import { getEmailOrMobileTaken, inviteAnotherParent, store } from 'src/api'
import EmailOrPhoneListInput from 'src/components/EmailOrPhoneListInput'
import FormikInput from 'src/components/FormikInput'
import LoadingUserContent from 'src/components/LoadingUserContent'
@@ -10,12 +10,29 @@ import { assertNotNull, assertNotUndefined } from 'src/helpers/util'
import { F7Props } from 'src/types'
import * as Yup from 'yup'
import { values } from 'lodash'
+import SubmitHandler from 'src/helpers/SubmitHandler'
export default function InviteOtherParentPage(props: F7Props) {
const { userId } = props.f7route.params
assertNotUndefined(userId)
const emailOrMobileRef = useRef(null)
+ const submitHandler = new SubmitHandler(f7)
+
+ const handleSubmit = useCallback((values: UserForm) => {
+ submitHandler.submit(() => {
+ return inviteAnotherParent(values).then(() => {
+ f7.dialog.alert(
+ tr({
+ en: `Thank you! An account has been created for ${values.firstName}. They can access their account by resetting their password or requesting a magic sign-in link. They should receive an email with these instructions shortly too.`,
+ es: `¡Gracias! Se ha creado una cuenta para ${values.firstName}. Pueden acceder a su cuenta restableciendo su contraseña o solicitando un enlace mágico de inicio de sesión. También deberían recibir un correo electrónico con estas instrucciones en breve.`,
+ reviewTrans: true,
+ }),
+ )
+ })
+ })
+ }, [])
+
const formik = useFormik({
validationSchema: Yup.object().shape({
firstName: Yup.string()
@@ -27,11 +44,48 @@ export default function InviteOtherParentPage(props: F7Props) {
emailOrMobile: Yup.string().required(),
}),
initialValues: new UserForm(),
- onSubmit: (values) => {
+ onSubmit: async (values) => {
if (!emailOrMobileRef.current?.validate(values.emailOrMobile)) {
return
}
- console.log('submit', values)
+
+ let emailOrMobileTaken: boolean = false
+
+ await submitHandler.submit(async () => {
+ emailOrMobileTaken = await getEmailOrMobileTaken(values.emailOrMobile)
+ })
+
+ if (emailOrMobileTaken) {
+ let message: string
+ if (values.emailOrMobile.includes('@')) {
+ message = tr({
+ en:
+ 'The email you provided is already in use. By continuing, you will allow the person with this account to access your children',
+ es:
+ 'El correo electrónico que proporcionó ya está en uso. Al continuar, permitirá que la persona con esta cuenta acceda a sus hijos.',
+ reviewTrans: true,
+ })
+ } else {
+ message = tr({
+ en:
+ 'The mobile number you provided is already in use. By continuing, you will allow the person with this account to access your children',
+ es:
+ 'El número de móvil que proporcionó ya está en uso. Al continuar, permitirá que la persona con esta cuenta acceda a sus hijos.',
+ reviewTrans: true,
+ })
+ }
+
+ f7.dialog.confirm(
+ message,
+ tr({ en: 'Account already exists', es: 'la cuenta ya existe', reviewTrans: true }),
+ () => {
+ handleSubmit(values)
+ },
+ () => {},
+ )
+ } else {
+ handleSubmit(values)
+ }
},
})
diff --git a/client/src/pages/users/SettingsPage.tsx b/client/src/pages/users/SettingsPage.tsx
index 5a4da49..ed2fce8 100644
--- a/client/src/pages/users/SettingsPage.tsx
+++ b/client/src/pages/users/SettingsPage.tsx
@@ -54,17 +54,6 @@ export default function SettingsPage() {
)}
- {currentUser.isParent() && (
-
- )}
-