diff --git a/.env b/.env index 9fdddf74..f8aa41dc 100644 --- a/.env +++ b/.env @@ -1,17 +1,26 @@ # # This is the default env config. -# Please create a `.env.local` file (you can copy the .env.local.example file) +# Please create a `.env.local` file +# (you can copy and edit the .env.local.example file) # to override this vars with your own configuration. # # See https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use -# ========================================================================= +# ============================================================================= # Port for the API webserver PORT=3232 API_HOST=api.luua.io +APP_HOST=luua.io -# If you want to setup a ngrox prox +# Override in .env.local if you want to setup a ngrox prox NGROK_TUNNEL=false + +# Override in .env.local +# if you want to deliver emails (for real) during local development +LOCAL_MAILER=false + +# The sender of the mails +MAIL_SENDER="hello@luua.io" diff --git a/.env.local.example b/.env.local.example index 0b096ca6..de27cfbd 100644 --- a/.env.local.example +++ b/.env.local.example @@ -21,3 +21,12 @@ NGROK_INSPECT= # DSN for sentry errors SENTRY_DSN= + +# SMTP parameters +SMTP_ADDRESS= +SMTP_PORT= +SMTP_USER_NAME= +SMTP_PASSWORD= +SMTP_DOMAIN= +SMTP_AUTHENTICATION= +SMTP_ENABLE_STARTTLS_AUTO= \ No newline at end of file diff --git a/app/controllers/api/workspace_invitations_controller.rb b/app/controllers/api/workspace_invitations_controller.rb index 2c665dd5..be7c2587 100644 --- a/app/controllers/api/workspace_invitations_controller.rb +++ b/app/controllers/api/workspace_invitations_controller.rb @@ -79,7 +79,6 @@ def create end end - def workspace_invitation_params params.require(:workspace_invitation).permit( :id, diff --git a/app/controllers/api/workspace_users_controller.rb b/app/controllers/api/workspace_users_controller.rb index dd136314..8e74198a 100644 --- a/app/controllers/api/workspace_users_controller.rb +++ b/app/controllers/api/workspace_users_controller.rb @@ -59,7 +59,6 @@ def create end end - def workspace_user_params params.require(:workspace_user).permit( :id, diff --git a/app/interactors/accept_workspace_invitation.rb b/app/interactors/accept_workspace_invitation.rb index 0a033659..485c07d4 100644 --- a/app/interactors/accept_workspace_invitation.rb +++ b/app/interactors/accept_workspace_invitation.rb @@ -1,6 +1,6 @@ -# +# # When a user accept an invitation to join a workspace -# +# class AcceptWorkspaceInvitation include Interactor @@ -46,7 +46,6 @@ def replace_notification!(context) end def add_member!(context) - # We add the user to the workspace wu = WorkspaceUser.new( workspace: context.workspace_invitation.workspace, diff --git a/app/interactors/reject_workspace_invitation.rb b/app/interactors/reject_workspace_invitation.rb index 5613bff4..49780cc7 100644 --- a/app/interactors/reject_workspace_invitation.rb +++ b/app/interactors/reject_workspace_invitation.rb @@ -1,6 +1,6 @@ -# +# # When a user reject an invitation to join a workspace -# +# class RejectWorkspaceInvitation include Interactor diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 00000000..e8f1850d --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,10 @@ +class UserMailer < Devise::Mailer + + include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url` + default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views + # If there is an object in your application that returns a contact email, you can use it as follows + # Note that Devise passes a Devise::Mailer object to your proc, hence the parameter throwaway (*). + default from: ->(*) { Class.instance.email_address } + + # @TODO remove hardcoded frontend url in mailer template, and find a better solution +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 1dfa4f88..d474fc87 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -9,7 +9,7 @@ def admin_ability(_user) end # For normal authed users - def regular_ability(user) + def regular_ability(user) # rubocop:todo Metrics/AbcSize can :manage, User, id: user.id can :read, Skill, skill_type: :global can :read, Skill, skill_type: :organization, organization: { id: user.organization_ids } @@ -25,7 +25,7 @@ def regular_ability(user) can %i[create update destroy], Mission, created_by: user.id can %i[manage], MissionUser, mission: { workspace: { id: user.admin_workspace_ids } } - can %i[accept complete reject], MissionUser, user_id: user.id + can %i[complete reject], MissionUser, user_id: user.id can %i[read], MissionUser, user_id: user.id can %i[apply], Mission diff --git a/app/serializers/notification_serializer.rb b/app/serializers/notification_serializer.rb index c2d305a2..b1ee04a7 100644 --- a/app/serializers/notification_serializer.rb +++ b/app/serializers/notification_serializer.rb @@ -33,7 +33,7 @@ def resource when 'MissionUser' MissionUserLightSerializer.new.serialize(object.resource) when 'WorkspaceInvitation' - puts "~~~~~~~~~~~~ß" + puts '~~~~~~~~~~~~ß' puts object.inspect puts object.resource.inspect WorkspaceInvitationSerializer.new.serialize(object.resource) diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 00000000..2b6cbd3f --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', "#{ENV['APP_HOST']}/users/confirm/#{@token}" %>

\ No newline at end of file diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 00000000..32f4ba80 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @email %>!

+ +<% if @resource.try(:unconfirmed_email?) %> +

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 00000000..b41daf47 --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

Hello <%= @resource.email %>!

+ +

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 00000000..f667dc12 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

Someone has requested a link to change your password. You can do this through the link below.

+ +

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 00000000..41e148bf --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 00000000..cbd34d2e --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 00000000..37f0bddb --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 36ad03a6..49b937a9 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -e7TzobyqYxdCH+HCAKjK718zhARDtc3ZW3mQQMJO86zcqq2f8eWa0h9OR1duey5mrga/IVOnQieNj7EtCE72lmKvl6kGOLgz+VyHw/bSEa9X8mcVkWXd+0wTHrDhTa7Pwb3wnydJorhG7K06WZ+haHBhzetX/MFcXjNxMjkeQtkbKNra47/Ps3+xMiBoLZrr7DfefaKRGUCtpTPpqblmW3WBkj/NyU+GGJnGJxT3OkeHdSZ/R15h8+a1ENgMCpkbyKuEsHz9dI1sVfMpQj6CzSqrj8PHo2f8UOI5Tmln71CoF1TiO3h8YxTAFfPbVmlbS+bf2e6DOGClczc=--70RfF1kKSVQLzYQI--RDOXXs2CbclT/g7zTW7Png== \ No newline at end of file +c/7pDprpYYeryanEW0CEKwqhUs1Nj/vSzqSzOy5cWAokny5lfWmwR5jcJ0gfIdzypgJnSKlweDN1qYqLhkk8duNQCkw7/hf2iTBMHEl7EvezepUcnqdatN+39kHOiMqMZE6t5npIqniFdJ2rlWUBWh647oJVwdS7niEVHO5JnMJBFHDB6kTUlNqEbGPVAuObEqieb61NS8lLlvEJtzgQQneXgpM/X+lDWN9pbyeneciHcO/CoZGrOnSf5YZBOqzdN7jw6svb7ezB0qSp8v3h586NuDeTTZGiVVya5GuW6ymPNG9ttc5GynYtXH/B0usuHDr2ks/vTpEG0vEYYejnQGgF5sgqDfjsBn0fRhAMo60kpwBlTJsJuVN7rpgX2b0jwvuPhrA2mfNQl33J05m3wyedaUMc+1I0ojzRPS9q57G+GNtFqPZdbCwOJ2sBb2lodf0OAxlhVR0mRGs2e7+ZlMaNo3TJJO8Lw/CiQvhktQ5PB9EAZoWZI87Wx3qrZ8r50/k7ckJ0Tvi89a1j5TWtUgZfiTB8K/IL0iUqrBdhIH6DkB8g4/j4FT/MOW8NDgqo0IpX/VFj6p4GvWi/y8LQ8jb3sgbJxbw=--WYrB8lgql6krUgdQ--1L0cncORYZ2EZN6QRTSVQA== \ No newline at end of file diff --git a/config/environments/development.rb b/config/environments/development.rb index abc564ab..0ef41668 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -63,6 +63,25 @@ # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker + if ENV['LOCAL_MAILER'] == 'true' + puts '📩 Warning: Mailer is activated, mails will be sent for real' + + config.action_mailer.delivery_method = :smtp + config.action_mailer.perform_deliveries = true + + mail_metadatas = { + address: ENV['SMTP_ADDRESS'] || Rails.application.credentials.smtp_address, + port: ENV['SMTP_PORT'] || Rails.application.credentials.smtp_port, + user_name: ENV['SMTP_USER_NAME'] || Rails.application.credentials.smtp_user_name, + password: ENV['SMTP_PASSWORD'] || Rails.application.credentials.smtp_password, + domain: ENV['SMTP_DOMAIN'] || Rails.application.credentials.smtp_domain, + authentication: ENV['SMTP_AUTHENTICATION'] || Rails.application.credentials.smtp_authentication || 'login', + enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] || Rails.application.credentials.smtp_enable_starttls_auto || true + } + + config.action_mailer.smtp_settings = mail_metadatas + end + # If we want to boot ngrok and we're running a server # We create a tunnel and update our postmark hooks with it if ENV['NGROK_TUNNEL'] == 'true' && Rails.const_defined?('Server') diff --git a/config/environments/production.rb b/config/environments/production.rb index 7f18ad49..b89ce578 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,4 @@ -Rails.application.configure do +Rails.application.configure do # rubocop:todo Metrics/BlockLength # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. @@ -63,6 +63,21 @@ config.action_mailer.perform_caching = false config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } + config.action_mailer.default_url_options = { host: ENV['API_HOST'] || 'luua.io' } + config.action_mailer.delivery_method = :smtp + + mail_metadatas = { + address: ENV['SMTP_ADDRESS'] || Rails.application.credentials.smtp_address, + port: ENV['SMTP_PORT'] || Rails.application.credentials.smtp_port, + user_name: ENV['SMTP_USER_NAME'] || Rails.application.credentials.smtp_user_name, + password: ENV['SMTP_PASSWORD'] || Rails.application.credentials.smtp_password, + domain: ENV['SMTP_DOMAIN'] || Rails.application.credentials.smtp_domain, + authentication: ENV['SMTP_AUTHENTICATION'] || Rails.application.credentials.smtp_authentication || 'login', + enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] || Rails.application.credentials.smtp_enable_starttls_auto || true + } + + config.action_mailer.smtp_settings = mail_metadatas + # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 4a97a647..248417c4 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -28,7 +28,7 @@ def http_auth_body # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class # with default "from" parameter. - config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' + config.mailer_sender = ENV['MAIL_SENDER'] # Configure the class responsible to send e-mails. # config.mailer = 'Devise::Mailer' diff --git a/config/routes.rb b/config/routes.rb index af33a59e..53b2e990 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,7 +69,6 @@ patch :read_all, on: :collection end - resources :mission_users do concerns :mission_users_actions end diff --git a/frontend/components/LoginForm/LoginForm.tsx b/frontend/components/LoginForm/LoginForm.tsx index 4b45137f..35ec9676 100644 --- a/frontend/components/LoginForm/LoginForm.tsx +++ b/frontend/components/LoginForm/LoginForm.tsx @@ -13,9 +13,11 @@ import MessageBox from '../../elements/MessageBox/MessageBox'; import ROUTES from '../../routes/manage'; import Link from 'next/link'; -interface Props { } +interface Props { + email?: string +} -const SignupForm = () => { +const SignupForm = (props: Props) => { const { t } = useLocale() const Yup = YupWithLocale() @@ -47,7 +49,7 @@ const SignupForm = () => { }; const initialValues = { - email: '', + email: props.email || '', password: '', } diff --git a/frontend/components/NetworkBoudary/NetworkBoudary.tsx b/frontend/components/NetworkBoudary/NetworkBoudary.tsx index 88405122..fb6c8134 100644 --- a/frontend/components/NetworkBoudary/NetworkBoudary.tsx +++ b/frontend/components/NetworkBoudary/NetworkBoudary.tsx @@ -21,6 +21,7 @@ const NetworkBoundary = ( return (<>

Network error !

{error && error.message}

+ {error?.response?.data &&
{JSON.stringify(error?.response?.data, null, 2)}
} ) case 'loading': return (<> @@ -32,7 +33,15 @@ const NetworkBoundary = ( return <>{children} } default: - throw new Error("No status"); + if (children && data) { + return <>{children} + } else { + console.error("No status"); + return (<> +

Loading...

+ {error &&

{error.message}

} + ) + } } } diff --git a/frontend/i18n/locales/fr/common.json b/frontend/i18n/locales/fr/common.json index b2c8bbbc..d817f65e 100644 --- a/frontend/i18n/locales/fr/common.json +++ b/frontend/i18n/locales/fr/common.json @@ -217,6 +217,9 @@ "cancel": "Annuler", "back": "Retour", "user": { + "confirm": { + "title": "Votre adresse email a bien été confirmée !" + }, "sign-up": { "submit": "Créer mon compte", "no-account": "Pas encore de compte ?" diff --git a/frontend/pages/manage/[workspace_id]/candidates/[id]/index.tsx b/frontend/pages/manage/[workspace_id]/candidates/[id]/index.tsx index 73240b3f..bd225bfc 100644 --- a/frontend/pages/manage/[workspace_id]/candidates/[id]/index.tsx +++ b/frontend/pages/manage/[workspace_id]/candidates/[id]/index.tsx @@ -15,7 +15,10 @@ import { useLocale } from '../../../../../hooks/useLocale' const { manage } = routes const { workspace } = manage -const MissionUser = ( +/** + * Show the current mission status for a user + */ +const Candidate = ( { initialData, token }: { initialData: MissionUser, token?: string } ) => { @@ -41,8 +44,8 @@ const MissionUser = ( ) } -MissionUser.getInitialProps = async (ctx: NextPageContext) => { +Candidate.getInitialProps = async (ctx: NextPageContext) => { return await fetchInitialData(ctx, `/api/mission_users/${ctx.query.id}`) } -export default withAuthSync(MissionUser) \ No newline at end of file +export default withAuthSync(Candidate) \ No newline at end of file diff --git a/frontend/pages/manage/[workspace_id]/candidates/index.tsx b/frontend/pages/manage/[workspace_id]/candidates/index.tsx index 2d49d8fc..d4718200 100644 --- a/frontend/pages/manage/[workspace_id]/candidates/index.tsx +++ b/frontend/pages/manage/[workspace_id]/candidates/index.tsx @@ -9,7 +9,6 @@ import NetworkBoundary from '../../../../components/NetworkBoudary/NetworkBoudar import MissionUserList from '../../../../components/MissionUserList/MissionList' import ContentLayout from '../../../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../../../layouts/ManageLeftMenu/ManageLeftMenu' import PageTitle from '../../../../elements/PageTitle/PageTitle'; import WorkspaceHeader from '../../../../components/WorkspaceHeader/WorkspaceHeader'; import WorkspaceContext from '../../../../contexts/WorkspaceContext'; diff --git a/frontend/pages/manage/[workspace_id]/contributors/[id]/index.tsx b/frontend/pages/manage/[workspace_id]/contributors/[id]/index.tsx index 84a8788d..65781986 100644 --- a/frontend/pages/manage/[workspace_id]/contributors/[id]/index.tsx +++ b/frontend/pages/manage/[workspace_id]/contributors/[id]/index.tsx @@ -15,7 +15,10 @@ import { useLocale } from '../../../../../hooks/useLocale' const { manage } = routes const { workspace } = manage -const MissionUser = ( +/** + * Show the current mission status for a user + */ +const Contributor = ( { initialData, token }: { initialData: MissionUser, token?: string } ) => { @@ -39,8 +42,8 @@ const MissionUser = ( ) } -MissionUser.getInitialProps = async (ctx: NextPageContext) => { +Contributor.getInitialProps = async (ctx: NextPageContext) => { return await fetchInitialData(ctx, `/api/mission_users/${ctx.query.id}`) } -export default withAuthSync(MissionUser) \ No newline at end of file +export default withAuthSync(Contributor) \ No newline at end of file diff --git a/frontend/pages/manage/[workspace_id]/contributors/index.tsx b/frontend/pages/manage/[workspace_id]/contributors/index.tsx index dd5e2f2b..9ebba375 100644 --- a/frontend/pages/manage/[workspace_id]/contributors/index.tsx +++ b/frontend/pages/manage/[workspace_id]/contributors/index.tsx @@ -10,7 +10,6 @@ import NetworkBoundary from '../../../../components/NetworkBoudary/NetworkBoudar import MissionUserList from '../../../../components/MissionUserList/MissionList' import ContentLayout from '../../../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../../../layouts/ManageLeftMenu/ManageLeftMenu' import PageTitle from '../../../../elements/PageTitle/PageTitle'; import { useContext } from 'react'; import WorkspaceContext from '../../../../contexts/WorkspaceContext'; @@ -22,7 +21,7 @@ const { workspace } = manage /** * Will list all the contributors for our workspace's missions */ -const Candidates = ( +const Contributors = ( { initialData, token }: { initialData: MissionUser[], token?: string } ) => { @@ -47,10 +46,10 @@ const Candidates = ( ) } -Candidates.getInitialProps = async (ctx: any) => { +Contributors.getInitialProps = async (ctx: any) => { return await fetchInitialData( ctx, `/api/workspaces/${ctx.query.workspace_id}/mission_users/contributors` ) } -export default withAuthSync(Candidates) \ No newline at end of file +export default withAuthSync(Contributors) \ No newline at end of file diff --git a/frontend/pages/manage/[workspace_id]/index.tsx b/frontend/pages/manage/[workspace_id]/index.tsx index 8d27683b..ea0c53a1 100644 --- a/frontend/pages/manage/[workspace_id]/index.tsx +++ b/frontend/pages/manage/[workspace_id]/index.tsx @@ -5,13 +5,12 @@ import { useRouter } from 'next/router' import NetworkBoundary from '../../../components/NetworkBoudary/NetworkBoudary' import { NextPageContext } from 'next' import WorkspaceShow from '../../../components/WorkspaceShow/WorkspaceShow' -import { Layout } from 'antd' -const { Content } = Layout; -import ManageLeftMenu from '../../../layouts/ManageLeftMenu/ManageLeftMenu' import ContentLayout from '../../../layouts/ContentLayout/ContentLayout' import WorkspaceHeader from '../../../components/WorkspaceHeader/WorkspaceHeader' - +/** + * Show the requested workspace, as a member of it + */ const ShowWorkspace = ( { initialData, token }: { initialData: Workspace, token?: string } diff --git a/frontend/pages/manage/[workspace_id]/invitations/index.tsx b/frontend/pages/manage/[workspace_id]/invitations/index.tsx index 422a000c..669d6c68 100644 --- a/frontend/pages/manage/[workspace_id]/invitations/index.tsx +++ b/frontend/pages/manage/[workspace_id]/invitations/index.tsx @@ -17,8 +17,6 @@ import WorkspaceHeader from '../../../../components/WorkspaceHeader/WorkspaceHea import WorkspaceInvitationItem from '../../../../elements/WorkspaceInvitationItem/WorkspaceInvitationItem' import PageTitle from '../../../../elements/PageTitle/PageTitle' import List from '../../../../elements/List/List' -import WorkspaceUserActions from '../../../../elements/WorkspaceUserActions/WorkspaceUserActions' -import { remove, update } from '../../../../api/workspace_user' import WorkspaceInvitationModal from '../../../../components/WorkspaceInvitationModal/WorkspaceInvitationModal' @@ -37,20 +35,6 @@ const WorkspaceInvitations = ( ) const { currentWorkspace } = useContext(WorkspaceContext) - const onUserDelete = async (id: number) => { - await remove(id, token || '') - await refetch() - } - - const onUserAdmin = async (id: number) => { - await update({ id, admin: true }, token || '') - await refetch() - } - const onUserRegular = async (id: number) => { - await update({ id, admin: false }, token || '') - await refetch() - } - const itemStyle = { display: 'flex', justifyContent: 'space-between', diff --git a/frontend/pages/manage/[workspace_id]/missions/[id]/edit.tsx b/frontend/pages/manage/[workspace_id]/missions/[id]/edit.tsx index 948d06d6..08c75371 100644 --- a/frontend/pages/manage/[workspace_id]/missions/[id]/edit.tsx +++ b/frontend/pages/manage/[workspace_id]/missions/[id]/edit.tsx @@ -8,7 +8,6 @@ import NetworkBoundary from '../../../../../components/NetworkBoudary/NetworkBou import MissionForm from '../../../../../components/MissionForm/MissionForm'; import ContentLayout from '../../../../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../../../../layouts/ManageLeftMenu/ManageLeftMenu' import { useContext } from 'react' import WorkspaceContext from '../../../../../contexts/WorkspaceContext' import WorkspaceHeader from '../../../../../components/WorkspaceHeader/WorkspaceHeader' diff --git a/frontend/pages/manage/[workspace_id]/missions/[id]/index.tsx b/frontend/pages/manage/[workspace_id]/missions/[id]/index.tsx index 144acbcf..6c67fe1e 100644 --- a/frontend/pages/manage/[workspace_id]/missions/[id]/index.tsx +++ b/frontend/pages/manage/[workspace_id]/missions/[id]/index.tsx @@ -9,13 +9,11 @@ import { withAuthSync } from '../../../../../utils/auth' import NetworkBoundary from '../../../../../components/NetworkBoudary/NetworkBoudary' -import ManageLeftMenu from '../../../../../layouts/ManageLeftMenu/ManageLeftMenu' import ContentLayout from '../../../../../layouts/ContentLayout/ContentLayout' import WorkspaceMissionDetail from '../../../../../components/WorkspaceMissionDetail/WorkspaceMissionDetail' import { useContext } from 'react' import WorkspaceContext from '../../../../../contexts/WorkspaceContext' import WorkspaceHeader from '../../../../../components/WorkspaceHeader/WorkspaceHeader' -import PageTitle from '../../../../../elements/PageTitle/PageTitle' import { useLocale } from '../../../../../hooks/useLocale' const { manage } = routes @@ -23,7 +21,7 @@ const { workspace } = manage /** - * Show workspace missions + * Show the requested workspace mission */ const Mission = ( { initialData, token }: diff --git a/frontend/pages/manage/[workspace_id]/missions/index.tsx b/frontend/pages/manage/[workspace_id]/missions/index.tsx index 69c5d8a4..91b7a4b9 100644 --- a/frontend/pages/manage/[workspace_id]/missions/index.tsx +++ b/frontend/pages/manage/[workspace_id]/missions/index.tsx @@ -9,12 +9,14 @@ import { useRouter } from 'next/router'; import routes from '../../../../routes/manage' import Link from 'next/link' import ContentLayout from '../../../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../../../layouts/ManageLeftMenu/ManageLeftMenu' import WorkspaceHeader from '../../../../components/WorkspaceHeader/WorkspaceHeader'; import WorkspaceContext from '../../../../contexts/WorkspaceContext'; import PageTitle from '../../../../elements/PageTitle/PageTitle'; const { manage } = routes +/** + * The missions list of a workspace + */ const Missions = ( { initialData, token }: { initialData: LightMission[], token?: string } diff --git a/frontend/pages/manage/[workspace_id]/missions/new.tsx b/frontend/pages/manage/[workspace_id]/missions/new.tsx index 5e16ee7f..a16ddab2 100644 --- a/frontend/pages/manage/[workspace_id]/missions/new.tsx +++ b/frontend/pages/manage/[workspace_id]/missions/new.tsx @@ -2,11 +2,12 @@ import React, { useContext } from 'react' import { withAuthSync } from '../../../../utils/auth' import MissionSetup from '../../../../components/MissionSetup/MissionSetup' import ContentLayout from '../../../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../../../layouts/ManageLeftMenu/ManageLeftMenu' import WorkspaceContext from '../../../../contexts/WorkspaceContext' import WorkspaceHeader from '../../../../components/WorkspaceHeader/WorkspaceHeader' -import Link from 'next/link' +/** + * Form to create a new mission + */ const NewMission = (props: any) => { const { currentWorkspace } = useContext(WorkspaceContext) diff --git a/frontend/pages/manage/index.tsx b/frontend/pages/manage/index.tsx index 344b40b2..05aa0865 100644 --- a/frontend/pages/manage/index.tsx +++ b/frontend/pages/manage/index.tsx @@ -25,4 +25,3 @@ const ManagePage = () => { } export default withAuthSync(ManagePage) -// export default ManagePage diff --git a/frontend/pages/manage/workspaces/index.tsx b/frontend/pages/manage/workspaces/index.tsx index e05303e1..5a42df1f 100644 --- a/frontend/pages/manage/workspaces/index.tsx +++ b/frontend/pages/manage/workspaces/index.tsx @@ -5,10 +5,7 @@ import NetworkBoundary from '../../../components/NetworkBoudary/NetworkBoudary' import WorkspaceList from '../../../components/WorkspaceList/WorkspaceList' import { NextPageContext } from 'next' import Link from 'next/link' -import { Layout } from 'antd' -import LeftMenu from '../../../layouts/LeftMenu/LeftMenu' import ContentLayout from '../../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../../layouts/ManageLeftMenu/ManageLeftMenu' import PageTitle from '../../../elements/PageTitle/PageTitle' import { useLocale } from '../../../hooks/useLocale' import ROUTES from '../../../routes/manage' diff --git a/frontend/pages/users/confirm/[token]/index.tsx b/frontend/pages/users/confirm/[token]/index.tsx new file mode 100644 index 00000000..c0c9fb4e --- /dev/null +++ b/frontend/pages/users/confirm/[token]/index.tsx @@ -0,0 +1,49 @@ +import React, { useEffect } from 'react' +import { useRouter } from 'next/router' +import { useCollection, fetchInitialData} from '../../../../utils/http' +import { withAuthSync } from '../../../../utils/auth' +import NetworkBoundary from '../../../../components/NetworkBoudary/NetworkBoudary' +import ContentLayout from '../../../../layouts/ContentLayout/ContentLayout' +import { useLocale } from '../../../../hooks/useLocale'; +import MessageBox from '../../../../elements/MessageBox/MessageBox' +import LoginForm from '../../../../components/LoginForm/LoginForm' + +/** + * This is the page used to confirm the email address + * of a new user + */ +const UserConfirmation = ({ initialData }: any) => { + + const { query } = useRouter() + const { t } = useLocale() + + const response = useCollection( + `/users/confirmation?confirmation_token=${query.token}`, + '', { }, { initialData, retry: false, manual: true } + ) + + useEffect(() => { + response.refetch() + }, []) + + return (<> + {...response}> + + + {t('form.user.confirm.title')} + + + + + + + ) +} + +UserConfirmation.getInitialProps = async (ctx: any) => { + return await fetchInitialData( + ctx, `/users/confirmation?confirmation_token=${ctx.query.token}` + ) +} + +export default withAuthSync(UserConfirmation) \ No newline at end of file diff --git a/frontend/pages/users/login.tsx b/frontend/pages/users/login.tsx index 5e434788..bbe30270 100644 --- a/frontend/pages/users/login.tsx +++ b/frontend/pages/users/login.tsx @@ -1,5 +1,5 @@ import React from 'react' -import FullLoginForm from '../../components/LoginForm/LoginForm' +import LoginForm from '../../components/LoginForm/LoginForm' import ContentLayout from '../../layouts/ContentLayout/ContentLayout' interface UserLoginData { @@ -9,13 +9,13 @@ interface UserLoginData { } /** - * This is the login form component + * This is the login page */ const Login = () => { return ( - + ) } diff --git a/frontend/pages/users/notifications.tsx b/frontend/pages/users/notifications.tsx index c16f1763..21a8133c 100644 --- a/frontend/pages/users/notifications.tsx +++ b/frontend/pages/users/notifications.tsx @@ -1,21 +1,17 @@ import React, { useContext } from 'react' -import Router from 'next/router' -import api, { getHeaders, useCollection, cdnUrl, fetchInitialData} from '../../utils/http' -import nextCookie from 'next-cookies' +import { useCollection, fetchInitialData} from '../../utils/http' import { withAuthSync } from '../../utils/auth' import NetworkBoundary from '../../components/NetworkBoudary/NetworkBoudary' import NotificationList from '../../components/NotificationList/NotificationList' -import { NextPageContext } from 'next' import ContentLayout from '../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../layouts/ManageLeftMenu/ManageLeftMenu' import PageTitle from '../../elements/PageTitle/PageTitle' -import { Avatar, Typography, Button } from 'antd' -import PrimaryLink from '../../elements/PrimaryLink/PrimaryLink' -const { Title } = Typography; -import manage from '../../routes/manage'; +import { Button } from 'antd' import { useLocale } from '../../hooks/useLocale'; import UserContext from '../../contexts/UserContext' +/** + * The user's notifications page + */ const Notifications = ( { initialData, token }: { initialData: UserNotification[], token?: string } diff --git a/frontend/pages/users/profile.tsx b/frontend/pages/users/profile.tsx index 2fd519b2..d062b694 100644 --- a/frontend/pages/users/profile.tsx +++ b/frontend/pages/users/profile.tsx @@ -1,35 +1,22 @@ import React from 'react' import Router from 'next/router' -import api, { getHeaders, useCollection, cdnUrl} from '../../utils/http' +import api, { getHeaders, useCollection} from '../../utils/http' import nextCookie from 'next-cookies' import { withAuthSync } from '../../utils/auth' import NetworkBoundary from '../../components/NetworkBoudary/NetworkBoudary' import UserProfile from '../../components/UserProfile/UserProfile' import { NextPageContext } from 'next' import ContentLayout from '../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../layouts/ManageLeftMenu/ManageLeftMenu' -import PageTitle from '../../elements/PageTitle/PageTitle' -import { Avatar, Typography } from 'antd' -import PrimaryLink from '../../elements/PrimaryLink/PrimaryLink' -const { Title } = Typography; -import manage from '../../routes/manage'; -import { useLocale } from '../../hooks/useLocale'; const Profile = ( { initialData, token }: { initialData: AuthedUser, token?: string } ) => { - const { t } = useLocale() - const response = useCollection( `/api/me`, token, {}, { initialData } ) - const imgOrPlaceholder = response.data?.thumb_url ? - cdnUrl(response.data?.thumb_url) : - `https://robohash.org/${response.data?.username || 'default'}.png?size=200x200` - return ( @@ -41,6 +28,8 @@ const Profile = ( Profile.getInitialProps = async (ctx: NextPageContext) => { + // @TODO cleanup this mess + const { token } = nextCookie(ctx) const redirectOnError = () => diff --git a/frontend/pages/users/signup.tsx b/frontend/pages/users/signup.tsx index c6b6d000..a5c4f6d6 100644 --- a/frontend/pages/users/signup.tsx +++ b/frontend/pages/users/signup.tsx @@ -1,5 +1,4 @@ import React, { useState, useContext } from 'react' -import { signupWithCredentials } from '../../utils/auth' import Router from 'next/router' import UserContext from '../../contexts/UserContext' import SignupForm from '../../components/SignupForm/SignupForm' @@ -7,9 +6,9 @@ import ContentLayout from '../../layouts/ContentLayout/ContentLayout' /** - * This is the login form component + * This is the signup page */ -const Login = () => { +const Signup = () => { const { currentUser } = useContext(UserContext) @@ -24,4 +23,4 @@ const Login = () => { ) } -export default Login \ No newline at end of file +export default Signup \ No newline at end of file diff --git a/frontend/pages/users/skills.tsx b/frontend/pages/users/skills.tsx index 60ce811f..072f4d1d 100644 --- a/frontend/pages/users/skills.tsx +++ b/frontend/pages/users/skills.tsx @@ -3,14 +3,16 @@ import { withAuthSync } from '../../utils/auth' import SkillsForm from '../../components/SkillsForm/SkillsForm' import PageTitle from '../../elements/PageTitle/PageTitle' import ContentLayout from '../../layouts/ContentLayout/ContentLayout' -import ManageLeftMenu from '../../layouts/ManageLeftMenu/ManageLeftMenu' import { useLocale } from "../../hooks/useLocale"; +/** + * The user's skills page + */ const Skills = (props: any) => { const { t } = useLocale() return ( - }> + diff --git a/lib/tasks/populate.rake b/lib/tasks/populate.rake index ba3e9e22..916a4b51 100644 --- a/lib/tasks/populate.rake +++ b/lib/tasks/populate.rake @@ -1,8 +1,9 @@ namespace :populate do - - desc "Create some random users and add them to the given workspace" + + desc 'Create some random users and add them to the given workspace' task :workspace, [:workspace_id] => [:environment] do |task, args| raise unless Rails.env.development? + puts task.inspect puts args.inspect diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb new file mode 100644 index 00000000..957e12b6 --- /dev/null +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -0,0 +1,4 @@ +# Preview all emails at http://localhost:3000/rails/mailers/user_mailer +class UserMailerPreview < ActionMailer::Preview + +end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb new file mode 100644 index 00000000..ac8c5f14 --- /dev/null +++ b/spec/mailers/user_mailer_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe UserMailer, type: :mailer do + pending "add some examples to (or delete) #{__FILE__}" +end