-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding testimonial form example #24
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Extends form object to capture submission events and | ||
// automagically create other piece types | ||
module.exports = { | ||
handlers (self) { | ||
return { | ||
submission: { | ||
async createTestimonial (req, form, data) { | ||
self.apos.modules.testimonial.addTestimony(req, data) | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// modules/article/index.js | ||
module.exports = { | ||
extend: "@apostrophecms/piece-type", | ||
options: { | ||
label: "Testimonial", | ||
pluralLabel: "Testimonials", | ||
}, | ||
fields: { | ||
add: { | ||
names: { | ||
label: "Names", | ||
type: "string", | ||
}, | ||
emailaddress: { | ||
label: "Email address", | ||
type: "string", | ||
}, | ||
phonenumber: { | ||
label: "Phone Number", | ||
type: "string", | ||
}, | ||
testimonial: { | ||
label: "Testimonial", | ||
type: "string", | ||
}, | ||
helpneeded: { | ||
label: "Help needed", | ||
type: "string", | ||
}, | ||
photo: { | ||
// Photos are not uploaded via the website form to avoid upload issues, but can be added later | ||
label: "Photo", | ||
type: "area", | ||
options: { | ||
max: 1, | ||
widgets: { | ||
"@apostrophecms/image": {}, | ||
}, | ||
}, | ||
required: false, | ||
}, | ||
active: { | ||
label: "Active", | ||
type: "boolean", | ||
help: "Active testimonials can be viewed on the public website", | ||
}, | ||
}, | ||
group: { | ||
basics: { | ||
label: "Basics", | ||
fields: [ | ||
"title", | ||
"names", | ||
"emailaddress", | ||
"phonenumber", | ||
"testimonial", | ||
"helpneeded", | ||
], | ||
}, | ||
advanced: { | ||
label: "Photo", | ||
fields: ["photo"], | ||
}, | ||
utility: { | ||
fields: ["active"], | ||
}, | ||
}, | ||
}, | ||
columns: { | ||
add: { | ||
names: { | ||
label: "Names", | ||
}, | ||
emailaddress: { | ||
label: "Email", | ||
}, | ||
phonenumber: { | ||
label: "Phone", | ||
}, | ||
active: { | ||
label: "Active", | ||
}, | ||
}, | ||
}, | ||
filters: { | ||
add: { | ||
active: { | ||
label: "Active", | ||
inputType: "select", | ||
def: false, | ||
}, | ||
}, | ||
}, | ||
queries(self, query) { | ||
builders: { | ||
return { | ||
builders: { | ||
active: { | ||
// This is our filter to be able to see active or inactive in the manager | ||
def: null, | ||
safeFor: "public", | ||
finalize() { | ||
const active = query.get("active"); | ||
if (active === null) { | ||
return; | ||
} | ||
|
||
if (active) { | ||
query.and({ | ||
active: true, | ||
}); | ||
} else { | ||
query.and({ | ||
active: false, | ||
}); | ||
} | ||
}, | ||
launder(value) { | ||
return self.apos.launder.booleanOrNull(value); | ||
}, | ||
choices() { | ||
return [ | ||
{ | ||
value: true, | ||
label: "Active", | ||
}, | ||
{ | ||
value: false, | ||
label: "NOT active", | ||
}, | ||
{ | ||
value: null, | ||
label: "All items", | ||
}, | ||
]; | ||
}, | ||
}, | ||
}, | ||
}; | ||
} | ||
}, | ||
handlers(self, options) {}, | ||
components(self) { | ||
return { | ||
// Returning the five most recently created testimonies. | ||
async latest(req, data) { | ||
const articles = await self | ||
.find(req) | ||
.active(true) | ||
.sort({ createdAt: -1 }) | ||
.limit(data.max || 5) | ||
.toArray(); | ||
return { | ||
articles, | ||
}; | ||
}, | ||
}; | ||
}, | ||
methods(self) { | ||
return { | ||
async addTestimony(req, initialInfo) { | ||
// Generate a blank testimony data object. | ||
let newTestimony = self.newInstance(); | ||
// Add our initial information to the object. | ||
newTestimony = { | ||
...newTestimony, | ||
...initialInfo, | ||
}; | ||
// Assign some arbitrary title to this piece | ||
newTestimony.title = initialInfo["names"]; | ||
// Set "active" to false - testimonies must be approved before uploading | ||
newTestimony.active = false; | ||
Object.assign(newTestimony, { ...initialInfo }); | ||
// Insert the testimonial with the asynchronous `self.insert` method | ||
const insertResult = await self.insert(req, newTestimony); | ||
return insertResult; | ||
}, | ||
}; | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<div class="container mx-auto mx-2"> | ||
<div class="flex flex-col justify-around gap-4 py-2"> | ||
{% set cls = cycler("", "bg-gray-200") %} {% for testimony in | ||
data.testimonials %} | ||
<div | ||
class="{{ cls.next() }} order-2 rounded-md flex flex-col justify-between p-2 shadow-lg hover:shadow-xl cursor-pointer" | ||
> | ||
<h6> | ||
{{ testimony.createdAt | date('D MMM') }}: {{ testimony.title }} | ||
</h6> | ||
<p>{{ testimony.testimonial }}</p> | ||
</div> | ||
{% endfor %} | ||
</div> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Forms example - Testimonial | ||
This is an example of using a form submission to automatically create a piece type in your Apostrophe 3 project. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have to admit that even though this is a very clear sentence, it took me a couple of read-throughs to really understand what this example did. Do you think you can expand this introduction just a little? Something along the lines of, "The form module can be an easy way to collect information from front-end users. This example demonstrates how to take that information and use it to automatically populate the fields of an existing piece type in your Apostrophe 3 project." |
||
## Installation | ||
* Copy the `modules/@apostrophe/form` folder into your project | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Depending on how experienced people are with their code editor, moving folders about in their project can be a little daunting. There is a web site, that allows you to download individual folders from GitHub as zip files. Maybe we could add such a link for each folder to be downloaded. We should just add a small description of what will happen when you click on the link so that people aren't surprised. |
||
* Copy the `modules/testimonial` folder into your project | ||
* Add the testimonial module to app.js as `testimonial: {},` | ||
## Extending the form module | ||
* `form/index.js` adds the async function `createTestimonial`, which is called on form submission. This calls the `testimonial.addTestimony()` function to create our new testimonial piece. | ||
## The testimonial piece | ||
### `index.js` | ||
The fields `names`, `emailaddress`, `phonenumber`, `testimonial` and `helpneeded` need to be matched by fields in the created form. When the form is submitted, that data is passed to `addTestimony`, these fields build our new piece. There are two extra fields in the `testimonial` piece type: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say something like: "We will automatically fill five |
||
* `photo` - we want to be able to add a photograph of the person submitting the testimonial, but we don't want to expose our site to file uploads on this first implementation. | ||
* `active` - this is a flag that defaults to false when the testimonial is created. When approved by an editor or admin, changing this flag to true will allow the testimonial to appear in the display component. | ||
|
||
Finally in this piece, we have the `latest` component which returns a list of the active testimonials. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you could include a short snippet of code that shows how to use the component? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I'm not sure how many people would actually use this template, maybe it is worth cherry-picking the necessary Tailwind CSS and packaging it up for delivery in the
ui/src/index.scss
file.