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