diff --git a/app/assets/stylesheets/member-facing/global.scss b/app/assets/stylesheets/member-facing/global.scss new file mode 100644 index 0000000000..16705b0d48 --- /dev/null +++ b/app/assets/stylesheets/member-facing/global.scss @@ -0,0 +1,10 @@ +.container { + section { + padding: 2em 4%; + clear: both; + } + + .alternate-bg { + background-color: #ececec; + } +} diff --git a/app/javascript/async/email-ukparliament.js b/app/javascript/async/email-ukparliament.js new file mode 100644 index 0000000000..bd1982db91 --- /dev/null +++ b/app/javascript/async/email-ukparliament.js @@ -0,0 +1,16 @@ +// @flow +import React from 'react'; +import { render } from 'react-dom'; +import { EmailPluginConfig } from './interfaces'; +import EmailParliament from '../modules/EmailParliament'; + +type Options = { + el: HTMLElement, + config: EmailPluginConfig, +}; +export const init = (options: Options) => { + if (!options.config.active) return; + if (options.el) { + render(, options.el); + } +}; diff --git a/app/javascript/async/email-your-mp.js b/app/javascript/async/email-your-mp.js deleted file mode 100644 index 7e3cd50211..0000000000 --- a/app/javascript/async/email-your-mp.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; - -PluginProps = {}; -const PluginView = (props: PluginProps) => { - return
; -}; diff --git a/app/javascript/async/index.js b/app/javascript/async/index.js index 3049d5bdc4..03d15090f5 100644 --- a/app/javascript/async/index.js +++ b/app/javascript/async/index.js @@ -1,17 +1,53 @@ // @flow -import PluginError from '../util/plugin-error'; -const MODULES = { - 'email-your-mp': () => import('./email-your-mp'), +type PluginErrorOptions = { + name?: string, + message: string, }; -export default async (name: string, options: any) => { + +export class PluginError { + name: string; + message: string; + constructor(options: PluginErrorOptions) { + this.name = options.name || 'PluginError'; + this.message = options.message; + } + + toString() { + return `${this.name}: ${this.message}`; + } +} + +export const MODULES = { + 'email-ukparliament': () => import('./email-ukparliament'), +}; + +// Lists supported async modules +export const list = (): string[] => Object.keys(MODULES); + +// Loads an async module. +export const load = async (name: string, options: any) => { const loader = MODULES[name]; if (!loader) { throw new PluginError({ message: `Plugin "${name}" is not available or does not support dynamic loading.`, }); } + const plugin = await loader(); + plugin.init(options); +}; - const plugin = await loader(options); - plugin.load(options); +export const modules = { + // Attaches the async feature to the champaign global + // object. + setup(champaign: any) { + Object.assign(champaign, { + modules: { + load, + list, + }, + }); + }, }; + +export default modules; diff --git a/app/javascript/plugins/interfaces.js b/app/javascript/async/interfaces.js similarity index 60% rename from app/javascript/plugins/interfaces.js rename to app/javascript/async/interfaces.js index e054dc16fb..9b0bc14332 100644 --- a/app/javascript/plugins/interfaces.js +++ b/app/javascript/async/interfaces.js @@ -8,3 +8,8 @@ export interface PluginConfig { pluginId: number; title: string; } + +export interface EmailPluginConfig extends PluginConfig { + subject: string; + template: string; +} diff --git a/app/javascript/components/ProfilePicture/ProfilePicture.css b/app/javascript/components/ProfilePicture/ProfilePicture.css new file mode 100644 index 0000000000..8b8e69d009 --- /dev/null +++ b/app/javascript/components/ProfilePicture/ProfilePicture.css @@ -0,0 +1,6 @@ +.ProfilePicture { + border-radius: 50%; + width: 70px; + height: 70px; + border: 2px solid papayawhip; +} diff --git a/app/javascript/components/ProfilePicture/index.js b/app/javascript/components/ProfilePicture/index.js new file mode 100644 index 0000000000..bb7388190f --- /dev/null +++ b/app/javascript/components/ProfilePicture/index.js @@ -0,0 +1,17 @@ +// @flow +import React from 'react'; +import classnames from 'classnames'; +import './ProfilePicture.css'; + +type Props = { + src: string, + alt: string, + className?: string, +}; + +export default (props: Props) => { + if (!props.src) return null; + const classNames = classnames('ProfilePicture', props.className); + + return {props.alt}; +}; diff --git a/app/javascript/member-facing/index.js b/app/javascript/member-facing/index.js new file mode 100644 index 0000000000..292155caf8 --- /dev/null +++ b/app/javascript/member-facing/index.js @@ -0,0 +1,28 @@ +// @flow +import Petition from './backbone/petition'; +import PetitionAndScrollToConsent from './backbone/petition_and_scroll_to_consent'; +import DoubleOptIn from './double_opt_in'; +import Survey from './backbone/survey'; +import ActionForm from './backbone/action_form'; +import OverlayToggle from './backbone/overlay_toggle'; +import Thermometer from './backbone/thermometer'; +import Sidebar from './backbone/sidebar'; +import Notification from './backbone/notification'; +import SweetPlaceholder from './backbone/sweet_placeholder'; +import CampaignerOverlay from './backbone/campaigner_overlay'; + +export const setup = (champaign: any) => { + Object.assign(champaign, { + ActionForm, + CampaignerOverlay, + DoubleOptIn, + Notification, + OverlayToggle, + Petition, + PetitionAndScrollToConsent, + Sidebar, + Survey, + SweetPlaceholder, + Thermometer, + }); +}; diff --git a/app/javascript/modules/EmailParliament/EmailComposer.css b/app/javascript/modules/EmailParliament/EmailComposer.css new file mode 100644 index 0000000000..e1a4535eb2 --- /dev/null +++ b/app/javascript/modules/EmailParliament/EmailComposer.css @@ -0,0 +1,5 @@ +.EmailComposer { + max-width: 800px; + margin-left: auto; + margin-right: auto; +} diff --git a/app/javascript/modules/EmailParliament/EmailComposer.js b/app/javascript/modules/EmailParliament/EmailComposer.js new file mode 100644 index 0000000000..9fec0353d0 --- /dev/null +++ b/app/javascript/modules/EmailParliament/EmailComposer.js @@ -0,0 +1,50 @@ +// @flow +// $FlowIgnore +import React, { useState } from 'react'; +import SweetInput from '../../components/SweetInput/SweetInput'; +import FormGroup from '../../components/Form/FormGroup'; +import Editor from '../../components/EmailEditor/EmailEditor'; +import type { Target } from './index'; +import './EmailComposer.css'; + +type Props = { + postcode: string, + title: string, + subject: string, + template: string, + target: Target, + body?: string, +}; + +export default (props: Props) => { + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + + return ( +
+

{props.title}

+ + + + + + + + + +
+
+ ); +}; diff --git a/app/javascript/modules/EmailParliament/RepresentativeCard.css b/app/javascript/modules/EmailParliament/RepresentativeCard.css new file mode 100644 index 0000000000..232945bca8 --- /dev/null +++ b/app/javascript/modules/EmailParliament/RepresentativeCard.css @@ -0,0 +1,26 @@ +.RepresentativeCard { + display: flex; + margin-left: -4%; + margin-right: -4%; + padding: 8%; + background: white; + margin-top: 2em; + margin-bottom: 2em; +} + +.RepresentativeCard-copy { + margin: auto 1em; + font-size: 1.3em; +} +.RepresentativeCard-name { + font-weight: bold; +} +.RepresentativeCard-party { + color: darkgrey; +} +.RepresentativeCard-email { + font-size: 0.8em; + color: grey; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + +} diff --git a/app/javascript/modules/EmailParliament/RepresentativeCard.js b/app/javascript/modules/EmailParliament/RepresentativeCard.js new file mode 100644 index 0000000000..2038a2f281 --- /dev/null +++ b/app/javascript/modules/EmailParliament/RepresentativeCard.js @@ -0,0 +1,42 @@ +// @flow +import React from 'react'; +import ProfilePicture from '../../components/ProfilePicture'; +import type { Target } from './index'; +import './RepresentativeCard.css'; + +type Props = { + target?: Target, +}; + +export default (props: Props) => { + let target = props.target; + if (!target) target = test; + // const { target } = props; + + return ( +
+ +
+ {target.displayAs} + {target.party} +
+ email: {target.email} +
+
+ ); +}; + +const test = { + email: 'stella.creasy.mp@parliament.uk', + name: 'Stella Creasy', + displayAs: 'Stella Creasy', + listAs: 'Creasy, Stella', + party: 'Labour (Co-op)', + picture: + 'https://data.parliament.uk/membersdataplatform/services//images/MemberPhoto/4088/', + gender: 'F', + id: '4088', +}; diff --git a/app/javascript/modules/EmailParliament/SearchByPostcode.css b/app/javascript/modules/EmailParliament/SearchByPostcode.css new file mode 100644 index 0000000000..cb5e0a43a1 --- /dev/null +++ b/app/javascript/modules/EmailParliament/SearchByPostcode.css @@ -0,0 +1,8 @@ +.SearchByPostcode { + max-width: 450px; + margin: auto; +} +.SearchByPostcode .title { + margin: 1em 0; +} + diff --git a/app/javascript/modules/EmailParliament/SearchByPostcode.js b/app/javascript/modules/EmailParliament/SearchByPostcode.js new file mode 100644 index 0000000000..9204ebdc13 --- /dev/null +++ b/app/javascript/modules/EmailParliament/SearchByPostcode.js @@ -0,0 +1,74 @@ +// @flow +// $FlowIgnore +import React, { useState, useEffect } from 'react'; +import Button from '../../components/Button/Button'; +import SweetInput from '../../components/SweetInput/SweetInput'; +import FormGroup from '../../components/Form/FormGroup'; +import SearchError from './SearchError'; +import { search } from './api'; +import type { Target } from './index'; + +import './SearchByPostcode.css'; + +type Props = { + onChange: (target: Target) => void, +}; + +const SearchByPostcode = (props: Props) => { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(false); + const [postcode, setPostcode] = useState(null); + const [input, setInput] = useState(''); + + const reset = () => { + setLoading(); + }; + useEffect( + () => { + (async () => { + if (!postcode) return; + + setLoading(true); + setError(false); + + try { + const target = await search(postcode); + props.onChange(target); + } catch (e) { + setError(true); + } finally { + setLoading(false); + } + })(); + }, + [postcode] + ); + + const submit = (e: SyntheticEvent) => { + e.preventDefault(); + setPostcode(input); + }; + + return ( +
+
+ + +

Enter your UK postcode

+ +
+ + +
+ ); +}; + +export default SearchByPostcode; diff --git a/app/javascript/modules/EmailParliament/SearchError.css b/app/javascript/modules/EmailParliament/SearchError.css new file mode 100644 index 0000000000..440f6b0efe --- /dev/null +++ b/app/javascript/modules/EmailParliament/SearchError.css @@ -0,0 +1,7 @@ +.SearchError { + background: #fff; + color: orangered; + border: 2px solid crimson; + border-radius: 2px; + padding: 0.5em 1em; +} diff --git a/app/javascript/modules/EmailParliament/SearchError.js b/app/javascript/modules/EmailParliament/SearchError.js new file mode 100644 index 0000000000..a6e427c90e --- /dev/null +++ b/app/javascript/modules/EmailParliament/SearchError.js @@ -0,0 +1,19 @@ +// @flow +import React from 'react'; +import './SearchError.css'; + +type Props = { + error: boolean, +}; + +export default ({ error }: Props) => { + if (!error) return null; + return ( +
+

+ We couldn't find your MP. Check that your postcode is correct, or get in + touch with us if you think there might be an issue. +

+
+ ); +}; diff --git a/app/javascript/modules/EmailParliament/api.js b/app/javascript/modules/EmailParliament/api.js new file mode 100644 index 0000000000..295f811b4e --- /dev/null +++ b/app/javascript/modules/EmailParliament/api.js @@ -0,0 +1,22 @@ +// @flow +import type { Target } from './index'; + +export const search = async (postcode: ?string) => { + const result: Target = await fetch( + 'https://sls.sumofus.org/parliament-data/mps', + { + method: 'post', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify({ postcode }), + } + ).then(r => r.json()); + + console.log('result:', result); + + return result; +}; + +type SendEmailParams = {}; +export const sendEmail = async (params: SendEmailParams) => {}; diff --git a/app/javascript/modules/EmailParliament/index.js b/app/javascript/modules/EmailParliament/index.js new file mode 100644 index 0000000000..fffb5fc0f2 --- /dev/null +++ b/app/javascript/modules/EmailParliament/index.js @@ -0,0 +1,45 @@ +// @flow +// $FlowIgnore +import React, { useState } from 'react'; +import { search } from './api'; +import SearchByPostcode from './SearchByPostcode'; +import RepresentativeCard from './RepresentativeCard'; +import EmailComposer from './EmailComposer'; +import ComponentWrapper from '../../components/ComponentWrapper'; +import type { EmailPluginConfig } from '../../async/interfaces'; + +export type Target = { + displayAs: string, + email: string, + gender: string, + id: string, + listAs: string, + name: string, + party: string, + picture: string, +}; + +type Props = { + config: EmailPluginConfig, +}; + +const EmailParliament = (props: Props) => { + const [target, setTarget] = useState(null); + return ( +
+ + + + + +
+ ); +}; + +export default EmailParliament; diff --git a/app/javascript/packs/member_facing.js b/app/javascript/packs/member_facing.js index 28011bf0b7..4954292880 100644 --- a/app/javascript/packs/member_facing.js +++ b/app/javascript/packs/member_facing.js @@ -1,4 +1,4 @@ -// @flow +import I18n from 'champaign-i18n'; import 'whatwg-fetch'; import '../shared/show_errors'; import '../member-facing/registration'; @@ -7,55 +7,40 @@ import '../recommend_pages/recommend_pages'; import '../util/event_tracking'; import { mapValues, pick } from 'lodash'; +import flatten from 'flat'; import URI from 'urijs'; import configureStore from '../state'; -import Petition from '../member-facing/backbone/petition'; -import PetitionAndScrollToConsent from '../member-facing/backbone/petition_and_scroll_to_consent'; -import DoubleOptIn from '../member-facing/double_opt_in'; -import Survey from '../member-facing/backbone/survey'; -import ActionForm from '../member-facing/backbone/action_form'; -import OverlayToggle from '../member-facing/backbone/overlay_toggle'; -import Thermometer from '../member-facing/backbone/thermometer'; -import Sidebar from '../member-facing/backbone/sidebar'; -import Notification from '../member-facing/backbone/notification'; -import SweetPlaceholder from '../member-facing/backbone/sweet_placeholder'; -import CampaignerOverlay from '../member-facing/backbone/campaigner_overlay'; import redirectors from '../member-facing/redirectors'; import { FeaturesHelper } from '../state/features'; import DonationsThermometer from '../plugins/donations-thermometer'; import ProgressTracker from '../plugins/progress-tracker'; - +import asyncModules from '../async/'; +import * as legacy from '../member-facing/'; window.URI = URI; if (process.env.EXTERNAL_ASSETS_JS_PATH) { - import(process.env.EXTERNAL_ASSETS_JS_PATH); + // $FlowIgnore + require(process.env.EXTERNAL_ASSETS_JS_PATH); } window.champaign = window.champaign || {}; +// Legacy components (Backbone) +legacy.setup(window.champaign); + +// Async modules (module splitting) +asyncModules.setup(window.champaign); + const store = configureStore(window.champaign); Object.assign(window.champaign, { - ActionForm, - CampaignerOverlay, DonationsThermometer, - DoubleOptIn, - Notification, - OverlayToggle, - Petition, - PetitionAndScrollToConsent, ProgressTracker, - Sidebar, - Survey, - SweetPlaceholder, - Thermometer, redirectors, store, features: new FeaturesHelper(store), }); -import flatten from 'flat'; - I18n.flatTranslations = mapValues(I18n.translations, (value, key) => flatten( pick(value, [ diff --git a/app/javascript/util/plugin-error.js b/app/javascript/util/plugin-error.js deleted file mode 100644 index 3ae4575f33..0000000000 --- a/app/javascript/util/plugin-error.js +++ /dev/null @@ -1,19 +0,0 @@ -// @flow - -type PluginErrorOptions = { - name?: string, - message: string, -}; - -export default class PluginError { - name: string; - message: string; - constructor(options: PluginErrorOptions) { - this.name = options.name || 'PluginError'; - this.message = options.message; - } - - toString() { - return `${this.name}: ${this.message}`; - } -} diff --git a/app/models/plugins/email.rb b/app/models/plugins/email.rb index fc5504d6a6..4300ca15e4 100644 --- a/app/models/plugins/email.rb +++ b/app/models/plugins/email.rb @@ -1,3 +1,28 @@ +# == Schema Information +# +# Table name: plugins_emails +# +# id :bigint(8) not null, primary key +# active :boolean default(FALSE) +# from :string +# instructions :text +# ref :string +# spoof_member_email :boolean default(FALSE) +# subjects :string default([]), is an Array +# template :text +# test_email_address :string +# title :string default("") +# created_at :datetime not null +# updated_at :datetime not null +# page_id :bigint(8) +# registered_email_address_id :bigint(8) +# +# Indexes +# +# index_plugins_emails_on_page_id (page_id) +# index_plugins_emails_on_registered_email_address_id (registered_email_address_id) +# + class Plugins::Email < ApplicationRecord DEFAULTS = {}.freeze @@ -14,6 +39,7 @@ def liquid_data(_supplemental_data = {}) page_id: page_id, plugin_id: id, locale: page.language_code, + title: title, active: active, subject: subjects.sample, template: template diff --git a/app/views/pages/_packs.slim b/app/views/pages/_packs.slim index c91cdb0a67..34052489a7 100644 --- a/app/views/pages/_packs.slim +++ b/app/views/pages/_packs.slim @@ -1,5 +1,4 @@ - plugins.each do |plugin| - name = plugin.name.underscore - - puts "Plugin name: #{name}" = stylesheet_pack_tag(name) if pack_exists?(name, :stylesheet) = javascript_pack_tag(name) if pack_exists?(name, :javascript) diff --git a/app/views/plugins/emails/_email.liquid b/app/views/plugins/emails/_email.liquid index 5a390971e1..7f10c6628f 100644 --- a/app/views/plugins/emails/_email.liquid +++ b/app/views/plugins/emails/_email.liquid @@ -1,11 +1,12 @@ {% if plugins.email[ref].active %} -
hello
+
{% endif %} diff --git a/app/views/plugins/emails/_form.slim b/app/views/plugins/emails/_form.slim index 483ed78171..0795979a0d 100644 --- a/app/views/plugins/emails/_form.slim +++ b/app/views/plugins/emails/_form.slim @@ -25,7 +25,7 @@ .has-error strong = t('plugins.email_tool.test_email_warning') - / = render 'plugins/shared/subject_editor', name: name, f: f, plugin: plugin, tooltip: t('plugins.email_tool.tooltips.email_content') + = render 'plugins/emails/subject_editor', name: name, f: f, plugin: plugin, tooltip: t('plugins.email_tool.tooltips.email_content') .email-template-section .form-group diff --git a/app/views/plugins/emails/_subject_editor.slim b/app/views/plugins/emails/_subject_editor.slim new file mode 100644 index 0000000000..d9eceb0d49 --- /dev/null +++ b/app/views/plugins/emails/_subject_editor.slim @@ -0,0 +1,17 @@ +- list_editor_id = "#{name}_subjects" +.form-group class=list_editor_id + = label_with_tooltip f, :subjects, t('plugins.email_tool.subject'), tooltip + .form-element__choice-fields + - plugin.subjects << '' if plugin.subjects.empty? + - plugin.subjects.each_with_index do |subject, i| + .form-element__choice-field + = f.text_field :subjects, value: subject, multiple: true, class: 'form-control', autocomplete: "subject-#{i}" + a.form-element__remove-choice + small + span.glyphicon.glyphicon-remove + a.form-element__add-choice = t('form_elements.add_choice') + + javascript: + $(document).ready(function(){ + new window.ListEditor({el: '.#{list_editor_id}' }); + }); diff --git a/db/migrate/20190417165312_create_plugins_emails.rb b/db/migrate/20190417165312_create_plugins_emails.rb index 120ebc1945..35dd0f93bc 100644 --- a/db/migrate/20190417165312_create_plugins_emails.rb +++ b/db/migrate/20190417165312_create_plugins_emails.rb @@ -1,5 +1,18 @@ class CreatePluginsEmails < ActiveRecord::Migration[5.2] def change - create_table :plugins_emails, &:timestamps + create_table :plugins_emails do |t| + t.boolean :active, default: false + t.boolean :spoof_member_email, default: false + t.string :from + t.string :ref + t.string :subjects, array: true, default: [] + t.string :test_email_address + t.string :title, default: '' + t.text :template + t.text :instructions + t.references :page, index: true + t.references :registered_email_address + t.timestamps + end end end diff --git a/db/schema.rb b/db/schema.rb index 29514d0227..55cf1283e5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_04_04_164508) do +ActiveRecord::Schema.define(version: 2019_04_17_165312) do # These are extensions that must be enabled in order to support this database enable_extension "intarray" @@ -457,6 +457,21 @@ t.string "target_by_attributes", default: [], array: true end + create_table "plugins_email_by_lookups", force: :cascade do |t| + t.string "ref", default: "default" + t.bigint "page_id" + t.boolean "active", default: false + t.string "from" + t.string "subjects", default: [], array: true + t.text "body" + t.text "body_header" + t.text "body_footer" + t.string "test_email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["page_id"], name: "index_plugins_email_by_lookups_on_page_id" + end + create_table "plugins_email_pensions", id: :serial, force: :cascade do |t| t.string "ref" t.integer "page_id" @@ -511,6 +526,24 @@ t.index ["page_id"], name: "index_plugins_email_tools_on_page_id" end + create_table "plugins_emails", force: :cascade do |t| + t.boolean "active", default: false + t.boolean "spoof_member_email", default: false + t.string "from" + t.string "ref" + t.string "subjects", default: [], array: true + t.string "test_email_address" + t.string "title", default: "" + t.text "template" + t.text "instructions" + t.bigint "page_id" + t.bigint "registered_email_address_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["page_id"], name: "index_plugins_emails_on_page_id" + t.index ["registered_email_address_id"], name: "index_plugins_emails_on_registered_email_address_id" + end + create_table "plugins_fundraisers", id: :serial, force: :cascade do |t| t.string "title" t.string "ref" diff --git a/flow-typed/npm/react-redux_v5.x.x.js b/flow-typed/npm/react-redux_v5.x.x.js index 759ad5a56e..a3ed094f28 100644 --- a/flow-typed/npm/react-redux_v5.x.x.js +++ b/flow-typed/npm/react-redux_v5.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 502cfd4f5e95c6308f747cdf16dc93ce -// flow-typed version: 1751d5bf0a/react-redux_v5.x.x/flow_>=v0.68.0 +// flow-typed signature: b872fdc4da6b0c1021c55df6fab87e73 +// flow-typed version: f8afc4cfdd/react-redux_v5.x.x/flow_>=v0.68.0 <=v0.84.x declare module "react-redux" { import type { ComponentType, ElementConfig } from 'react'; diff --git a/spec/factories/plugins_emails.rb b/spec/factories/plugins_emails.rb index 73b4297907..49edea40bb 100644 --- a/spec/factories/plugins_emails.rb +++ b/spec/factories/plugins_emails.rb @@ -1,3 +1,28 @@ +# == Schema Information +# +# Table name: plugins_emails +# +# id :bigint(8) not null, primary key +# active :boolean default(FALSE) +# from :string +# instructions :text +# ref :string +# spoof_member_email :boolean default(FALSE) +# subjects :string default([]), is an Array +# template :text +# test_email_address :string +# title :string default("") +# created_at :datetime not null +# updated_at :datetime not null +# page_id :bigint(8) +# registered_email_address_id :bigint(8) +# +# Indexes +# +# index_plugins_emails_on_page_id (page_id) +# index_plugins_emails_on_registered_email_address_id (registered_email_address_id) +# + FactoryBot.define do factory :plugins_email, class: 'Plugins::Email' do end diff --git a/spec/models/plugins/email_spec.rb b/spec/models/plugins/email_spec.rb index 416d47ec3e..2616ee89f9 100644 --- a/spec/models/plugins/email_spec.rb +++ b/spec/models/plugins/email_spec.rb @@ -1,3 +1,28 @@ +# == Schema Information +# +# Table name: plugins_emails +# +# id :bigint(8) not null, primary key +# active :boolean default(FALSE) +# from :string +# instructions :text +# ref :string +# spoof_member_email :boolean default(FALSE) +# subjects :string default([]), is an Array +# template :text +# test_email_address :string +# title :string default("") +# created_at :datetime not null +# updated_at :datetime not null +# page_id :bigint(8) +# registered_email_address_id :bigint(8) +# +# Indexes +# +# index_plugins_emails_on_page_id (page_id) +# index_plugins_emails_on_registered_email_address_id (registered_email_address_id) +# + require 'rails_helper' RSpec.describe Plugins::Email, type: :model do