Skip to content
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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions examples/testimonials/modules/@apostrophecms/form/index.js
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)
}
}
}
}
}
180 changes: 180 additions & 0 deletions examples/testimonials/modules/testimonial/index.js
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;
},
};
},
};
15 changes: 15 additions & 0 deletions examples/testimonials/modules/testimonial/views/latest.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="container mx-auto mx-2">
Copy link
Contributor

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.

<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') }}:&nbsp;{{ testimony.title }}
</h6>
<p>{{ testimony.testimonial }}</p>
</div>
{% endfor %}
</div>
</div>
15 changes: 15 additions & 0 deletions examples/testimonials/readme.md
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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

@BoDonkey BoDonkey Jan 20, 2023

Choose a reason for hiding this comment

The 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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say something like: "We will automatically fill five string type fields in the testimony piece type. Each of these fields need to be in the form you present to the user. They are .... You should choose the input types to match the expected input in your form. When the form is submitted, ..."

* `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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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?