diff --git a/catalog/createAWorkEventWithAgent/README.md b/catalog/createAWorkEventWithAgent/README.md
new file mode 100644
index 0000000..9e0506c
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/README.md
@@ -0,0 +1,118 @@
+---
+title: Create A Work Event With Agent
+description: Enable employees to propose, manage, and register for work events via interactive UI views or an AI Custom Agent featuring Agent File Uploads and Custom Scripts in Skill Files.
+version: 2026.1
+---
+
+# Create A Work Event With Agent
+
+Enable employees to propose, manage, and register for work events using interactive UI views (including a multi-select calendar widget) or through an AI Custom Agent featuring **Agent File Uploads** and **Custom Scripts in Skill Files**.
+
+- **Version:** 2026.1
+
+---
+
+## 1. Resources
+
+### Extend App Overview
+
+- [Review the Create A Work Event forum page](https://forum.developer.workday.com/t/app-catalog-app-create-a-work-event/7301) for even more details and a video demo of the app.
+
+### Agent Feature References
+
+- [Feature Overview: Agent File Uploads in Custom Agents](https://forum.developer.workday.com/t/feature-overview-agent-file-uploads-in-custom-agents/47396)
+- [Feature Overview: Custom Scripts in Agent Skills](https://forum.developer.workday.com/t/feature-overview-custom-scripts-in-agent-skills/47481)
+
+---
+
+## 2. Deployment Instructions
+
+> ⚠️ **Important:** The application, its data models, and business process components **must be deployed first** to establish tenant-specific App Reference IDs and WIDs before configuring agent skill files.
+
+### Step 1: Package the Application
+1. Clone or download this repository.
+2. Compress (zip) the `createAWorkEventWithAgent` folder located in the `catalog/` directory.
+3. Use this `.zip` package when importing the application into Workday App Builder.
+
+### Step 2: Import & Deploy via App Builder
+1. Build an Extend App by choosing the `Upload a Zip File` option.
+2. Import the `createAWorkEventWithAgent` package.
+3. Deploy the application to your tenant.
+
+> ℹ️ **Expected Warning During Initial Deployment:**
+> You may see an **"Agent Registry Update Failed"** error modal stating:
+> *"The app was deployed successfully, but the agent could not be registered with the Agent System of Record."*
+> **This is expected and normal.** The underlying data model components and application deployed successfully. The agent registration fails at this stage because the skill `.md` files contain invalid placeholder WIDs and source tool names. You will fix these references in **Section 4**.
+
+---
+
+## 3. Security & Business Process Configuration
+
+### A. Domain Security Policies (App Manager)
+
+#### 1. Manage: Create an Event Security Domain
+1. In the **Model Components** section of App Manager, locate the Security Domain `Manage: Create an Event` and click **Create** in the **Domain Security Policy** column.
+2. Check `Confirm` and click **OK**.
+3. Add the following permissions:
+ - **Report/Task Permissions (Modify):** `HR Administrator`
+
+#### 2. Register For Events Security Domain
+1. In the **Model Components** section of App Manager, locate the Security Domain `Register For Events` and click **Create** in the **Domain Security Policy** column.
+2. Check `Confirm` and click **OK**.
+3. Add the following permissions:
+ - **Report/Task Permissions (Modify):** `All Employees`
+
+---
+
+### B. Business Process Configuration
+
+#### 1. Create a Work Event Business Process Security
+1. Locate the `Create Work Event` Business Process in App Manager and click **Edit** in the **Security Policy** column.
+2. Configure the Business Process Security Policy with the following permissions:
+ - **Initiate Create a Work Event:** `Employee as Self`
+ - **Action Steps: Book Event Space:** `HR Administrator`
+ - **View All:** `Employee as Self`, `HR Administrator`
+ - **Approve:** `Manager`
+ - **Deny:** `Manager`
+3. Run the **Activate Pending Security Policy Changes** task to activate your updates.
+
+#### 2. Business Process Definition
+1. Locate the `Create a Work Event` Business Process in App Manager and click **Create** in the **Business Process Definition** column. Click **OK**.
+2. On the **Edit Business Process** screen, click **OK** and **Done** to create an empty definition that auto-completes.
+ - *(Optional)* Add additional steps as needed (e.g., an approval step for **HR Administrators**).
+
+---
+
+## 4. Custom Agent Configuration & Activation
+
+### Step 1: Update Skill Files Using Developer Agent
+1. In **App Builder**, verify you are connected to your active tenant.
+2. Open and run **Developer Agent** using the following prompt:
+
+> *"The Agent Skill files were imported from an application outside the current organization. Please find all references in the skill files to `createAWorkEventWithAgent_mcwslt` and replace them with the current app reference ID. For any tool where you make this change, also update the tool with the valid WID for this tenant. Do not make changes to any other tools."*
+
+### Step 2: Re-Deploy the Application
+1. Save all updated skill files in App Builder.
+2. Deploy the application again to apply the updated, tenant-specific references.
+
+### Step 3: Register & Activate the Agent
+1. In Workday, navigate to the **Agent System of Record**.
+2. Register the newly configured **Work Event** agent.
+3. Verify that all skills, custom scripts, and tools are associated correctly.
+4. Set the agent status to **Active**.
+
+---
+
+## 5. Usage & Verification
+
+### Standard UI Usage
+1. In your Development tenant, search for and run the **Work Event Registration** task.
+2. **Create an Event:** Propose a new work event.
+3. **Manage Events:** Create or update existing Work Events.
+4. **Register for an Event:** Sign up for an existing Work Event (users may register only once per event; a charity must exist to use the Donations page).
+5. **Manage My Events:** View and manage existing event registrations.
+6. **Calendar View:** View, create, and register for work events using the Multi-Select Calendar widget.
+
+### Agent Verification (Workday Chat)
+1. In **Workday Tenant Agent Chat**, start a session with your newly registered Work Event agent.
+2. Test natural-language event creation (e.g., *"Help me create an Event for the Holiday Party"*).
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/agents/create-work-event.agentskill b/catalog/createAWorkEventWithAgent/agents/create-work-event.agentskill
new file mode 100644
index 0000000..013aefd
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/agents/create-work-event.agentskill
@@ -0,0 +1,124 @@
+---
+name: create-work-event
+description: "Guides an employee through creating a new Work Event in the app. Collects all required event details from the employee and submits the event via the app's Work Event REST endpoint."
+metadata:
+ tags:
+ - work-event
+ - create
+allowed-tools:
+ - find_staffing_workers
+ - post_create_awork_event_with_agent_mcwslt_create_work_event_events
+ - post_create_awork_event_with_agent_mcwslt_work_events
+ - agent_file_download
+tool-wids:
+ - name: find_staffing_workers
+ wid: 30aeb92af72e100063e2955a0eff0000
+ - name: post_create_awork_event_with_agent_mcwslt_create_work_event_events
+ wid: c31e2903c40655789159a63ebc07eafa
+ - name: post_create_awork_event_with_agent_mcwslt_work_events
+ wid: 69fb9612b1fa5196ac2c1aa6791e65d5
+ - name: agent_file_download
+ wid: 94fadfecb1bb100031d911f8ed0a0000
+---
+
+# Recurring Event Date Calculator
+
+When a user wants to create a **series of recurring Work Events**, use the sandbox to compute
+all event dates before collecting any other details.
+
+## Recurrence Detection
+
+Ask the user: *"Is this a one-time event or will it repeat on a schedule (e.g., weekly, monthly)?"*
+
+If the user indicates the event **recurs**, collect:
+- **Start Date** — the date of the first occurrence (YYYY-MM-DD)
+- **Recurrence Pattern** — one of: `daily`, `weekly`, `biweekly`, `monthly`
+- **Number of Occurrences** — how many times the event should repeat (including the first)
+
+## Policy Enforcement — 3-Month Limit
+
+**Company policy requires that no occurrence may be scheduled more than 3 months in the future from today's date.**
+
+Once you have the recurrence parameters, **execute the sandbox script below immediately — do not attempt to evaluate or filter dates yourself.** The script is the sole enforcer of the 3-month rule. Only after the script has run and returned its output should you read the `KEPT`, `DROPPED`, and `MAX_ALLOWED` values and relay the results to the user, e.g.:
+
+> *"Based on company policy, occurrences cannot be scheduled more than 3 months in the future (cutoff: {MAX_ALLOWED}). Your series has been trimmed to {KEPT} occurrences. {DROPPED} occurrence(s) beyond that date were removed."*
+
+Execute the following Python script in the sandbox to calculate all occurrence dates:
+
+```python
+from datetime import date, timedelta
+from dateutil.relativedelta import relativedelta
+
+start_date = date.fromisoformat("{START_DATE}") # e.g. "2025-09-01"
+pattern = "{PATTERN}" # daily | weekly | biweekly | monthly
+occurrences = {OCCURRENCES} # integer, e.g. 6
+
+deltas = {
+ "daily": timedelta(days=1),
+ "weekly": timedelta(weeks=1),
+ "biweekly": timedelta(weeks=2),
+}
+
+from datetime import date
+max_date = date.today() + relativedelta(months=3)
+
+dates = []
+dropped = 0
+for i in range(occurrences):
+ if pattern == "monthly":
+ d = start_date + relativedelta(months=i)
+ else:
+ d = start_date + deltas[pattern] * i
+ if d <= max_date:
+ dates.append(d.isoformat())
+ else:
+ dropped += 1
+
+print(f"KEPT={len(dates)}")
+print(f"DROPPED={dropped}")
+print(f"MAX_ALLOWED={max_date.isoformat()}")
+for d in dates:
+ print(d)
+```
+
+- Replace `{START_DATE}`, `{PATTERN}`, and `{OCCURRENCES}` with the user's values before executing.
+- The script prints one ISO date per line — capture the output as the list of event dates.
+
+## Creating the Series
+
+Present the full list of computed dates to the user and ask them to confirm (or remove any dates
+they don't want). For each **confirmed date**:
+
+1. Collect any event details that may vary per occurrence (e.g., end date / time), then
+ repeat **Steps 2–7** from the main flow below once per date, creating a separate Work Event
+ instance for each occurrence.
+2. Notify the user of progress after each creation (e.g., *"Event 2 of 6 created ✓"*) and
+ provide a final summary listing all created events when the series is complete.
+
+---
+
+# Create Work Event Skill
+
+This skill helps an employee create a new Work Event. Collect all required details
+conversationally, confirm with the user, then submit via the Work Event API.
+
+## Steps
+
+1. Greet the user and let them know you'll help them create a new Work Event. Ask if they have an **event flyer** they'd like to upload — if so, prompt them to share the image in chat.
+2. If a flyer was provided:
+ - Use `agent_file_download` to download the image.
+ - Use your built-in OCR capabilities to read all text visible in the image, then extract as many of the following event details as possible:
+ - **Name** of the event
+ - **Description**
+ - **Location**
+ - **Start Date** (YYYY-MM-DD)
+ - **End Date** (YYYY-MM-DD)
+ - **Event Sponsor**
+ - **Contact Info / Email**
+ - **Internal Only?** (yes/no)
+ - Present the extracted details to the user and ask them to confirm or correct any fields.
+3. For any details not found in the flyer (or if no flyer was provided), conversationally collect the missing required fields from the user.
+4. Confirm all collected details with the user before submitting.
+5. Use `post_create_awork_event_with_agent_mcwslt_work_events` to create the WorkEvent model instance. Capture the `id` (WID) returned in the response.
+6. Using the `id` from step 5, call `post_create_awork_event_with_agent_mcwslt_create_work_event_events` to trigger the **CreateWorkEvent** business process for the newly created event instance.
+7. Confirm to the user that the event has been created and the business process has been initiated, sharing the event name and any relevant details from the response.
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/agents/edit-work-event.agentskill b/catalog/createAWorkEventWithAgent/agents/edit-work-event.agentskill
new file mode 100644
index 0000000..8198019
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/agents/edit-work-event.agentskill
@@ -0,0 +1,52 @@
+---
+name: edit-work-event
+description: >
+ Helps an employee edit an existing Work Event. Loads the current event data,
+ allows the employee to update fields, and saves the changes via the app's
+ Work Event REST endpoint.
+metadata:
+ tags:
+ - work-event
+ - edit
+allowed-tools:
+ - get_create_awork_event_with_agent_mcwslt_work_events
+ - patch_create_awork_event_with_agent_mcwslt_work_events
+tool-wids:
+ - name: get_create_awork_event_with_agent_mcwslt_work_events
+ wid: "2eccfb57d2e55a69843387ddf09e5fb2"
+ - name: patch_create_awork_event_with_agent_mcwslt_work_events
+ wid: "b93e2bc6b4dc5a249842536fe9b711b1"
+---
+
+# Edit Work Event Skill
+
+This skill helps an employee edit an existing Work Event. The event should already
+have been identified (via the find-work-event skill or provided by the user).
+
+## Steps
+
+1. If a `workEventId` has been provided (e.g., passed from a find-work-event flow),
+ proceed to step 2. Otherwise, ask the user to provide the Work Event ID they want
+ to edit.
+2. Call `get_create_awork_event_with_agent_mcwslt_work_events` with the event ID as
+ `contextInstanceId` to load the current event details.
+3. Conversationally collect any fields the user wants to update:
+ - **Name** of the event
+ - **Description**
+ - **Location**
+ - **Start Date** (YYYY-MM-DD)
+ - **End Date** (YYYY-MM-DD)
+ - **Event Sponsor**
+ - **Contact Info / Email**
+ - **Internal Only?** (yes/no)
+ Present the current values to the user and ask which fields they would like to change.
+
+4. Confirm the changes with the user, then update the event using `patch_create_awork_event_with_agent_mcwslt_work_events`.
+
+## Error Handling
+
+- If the event ID is not found (404), inform the user and suggest using the
+ find-work-event skill to locate the correct event.
+- If the API returns a validation error, relay the message to the user and prompt
+ them to correct the field.
+- If the user declines confirmation, ask if they'd like to adjust their changes or cancel.
diff --git a/catalog/createAWorkEventWithAgent/agents/find-work-event.agentskill b/catalog/createAWorkEventWithAgent/agents/find-work-event.agentskill
new file mode 100644
index 0000000..16161b3
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/agents/find-work-event.agentskill
@@ -0,0 +1,50 @@
+---
+name: find-work-event
+description: >
+ Helps an employee search for and locate Work Events in the app. Returns a list
+ of matching events so the employee can identify the one they want to view or edit.
+metadata:
+ tags:
+ - work-event
+ - search
+ - find
+allowed-tools:
+ - get_create_awork_event_with_agent_mcwslt_work_events
+tool-wids:
+ - name: get_create_awork_event_with_agent_mcwslt_work_events
+ wid: "2eccfb57d2e55a69843387ddf09e5fb2"
+---
+
+# Find Work Event Skill
+
+This skill helps an employee search for existing Work Events. The employee can search
+by name, date range, or browse available events, and then select one to view or pass
+to the edit-work-event skill.
+
+## Steps
+
+1. Ask the user how they would like to find the event. Options include:
+ - **By name** — ask for the event name or partial name to search
+ - **Browse all** — list available events
+2. Call `get_create_awork_event_with_agent_mcwslt_work_events` to retrieve Work Events.
+ - If the user provided a search term, pass it as a `search` parameter if supported,
+ or retrieve a list and filter by name in the response.
+ - Use `limit` and `offset` to paginate if needed (default limit 20).
+3. Present the results to the user as a numbered or bulleted list, showing for each:
+ - Event Name
+ - Start Date / End Date
+ - Location
+ - Event ID
+4. Ask the user which event they are interested in. If no results match, let the user
+ know and offer to broaden the search or try different keywords.
+5. Once the user selects an event, confirm the selection and offer next steps:
+ - **Edit this event** — hand off the event ID to the edit-work-event skill
+ - **View details** — hand off the event ID to the view-work-event skill
+ - **Search again** — restart the search
+
+## Error Handling
+
+- If the API returns no results, inform the user clearly and suggest broadening the
+ search criteria.
+- If the API returns an error, relay the message to the user and suggest retrying.
+- If the user wants to navigate to more than 20 results, offer to load the next page.
diff --git a/catalog/createAWorkEventWithAgent/agents/view-work-event.agentskill b/catalog/createAWorkEventWithAgent/agents/view-work-event.agentskill
new file mode 100644
index 0000000..30b9c56
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/agents/view-work-event.agentskill
@@ -0,0 +1,47 @@
+---
+name: view-work-event
+description: "Displays the details of a single Work Event to the employee in the agent chat window and allows them to register for that event directly from the conversation."
+metadata:
+ tags:
+ - work-event
+ - view
+ - register
+allowed-tools:
+ - get_create_awork_event_with_agent_mcwslt_work_events
+ - patch_create_awork_event_with_agent_mcwslt_work_events
+ - post_create_awork_event_with_agent_mcwslt_registrants
+tool-wids:
+ - name: get_create_awork_event_with_agent_mcwslt_work_events
+ wid: 2eccfb57d2e55a69843387ddf09e5fb2
+ - name: patch_create_awork_event_with_agent_mcwslt_work_events
+ wid: b93e2bc6b4dc5a249842536fe9b711b1
+ - name: post_create_awork_event_with_agent_mcwslt_registrants
+ wid: b27ef2bf4ad958c8b006bdcc36da277d
+---
+
+# View Work Event Skill
+
+This skill displays the full details of a specific Work Event in the agent chat window and allows the employee to register for it or cancel.
+
+## Steps
+
+1. When the user asks to view a specific Work Event — for example "show me the details for [Event Name]" or after clicking a Register button from the `registerForEventsAui` list page — identify the `workEventId` for the event.
+ - If the user has not yet specified which event they want to view, ask them to clarify or use the `register-for-events` skill to help them browse upcoming events first.
+2. Call `get_create_awork_event_with_agent_mcwslt_work_events` with the `workEventId` to load the event details.
+3. Present the following event details to the user conversationally:
+ - Event name
+ - Date range (start date — end date)
+ - Location
+ - Description
+ - Sponsor
+ - Fee
+ - Contact information
+4. Ask the user if they would like to **Register** for the event or if they want to do something else:
+ - If the user confirms registration, call `post_create_awork_event_with_agent_mcwslt_registrants` to create a Registrant record and confirm success: "You have successfully registered for **[Event Name]**!"
+ - If the user declines, acknowledge their choice and end the skill gracefully: "No problem! Let me know if there's anything else I can help with."
+ - If the user is already registered, inform them: "It looks like you're already registered for this event."
+
+## Error Handling
+
+- If the event page fails to load, inform the user and suggest trying again later.
+- If registration fails, relay the error and suggest contacting a system administrator.
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/agents/work-event-agent.agent b/catalog/createAWorkEventWithAgent/agents/work-event-agent.agent
new file mode 100644
index 0000000..6de5ff7
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/agents/work-event-agent.agent
@@ -0,0 +1,33 @@
+---
+name: work-event-agent
+description: "An intelligent assistant that helps employees create, find, and edit Work Events. Employees can ask to create a new Work Event, search for existing events, or update event details — all through a conversational interface."
+skills:
+ - create-work-event
+ - find-work-event
+ - edit-work-event
+ - view-work-event
+---
+
+# System Prompt
+
+You are the Work Event Agent, an intelligent assistant built on the Workday Agent platform.
+Your purpose is to help employees create new Work Events, locate existing Work Events, and
+edit Work Event details — all through a friendly, guided conversational experience.
+
+## Guidelines
+
+- Use your available skills to accomplish the user's goals efficiently.
+- For **creating** a new Work Event, use the `create-work-event` skill.
+- For **finding or searching** for a Work Event, use the `find-work-event` skill.
+- For **editing** an existing Work Event, use the `edit-work-event` skill.
+- For **registering** for an upcoming Work Event, use the `register-for-events` skill.
+- For **viewing details** of a specific Work Event and registering from the detail page, use the `view-work-event` skill.
+- If a user asks to edit a Work Event but hasn't identified which one, use the
+ `find-work-event` skill first to help them locate it, then proceed to edit.
+- Ask for clarification when the user's intent is ambiguous (e.g., "Would you like to
+ create a new event or update an existing one?").
+- Always present a summary and ask for confirmation before taking any irreversible
+ action (submitting or updating data).
+- If you cannot fulfill a request with your current skills, explain what you can do and
+ suggest next steps.
+- Keep responses concise and professional.
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/appManifest.json b/catalog/createAWorkEventWithAgent/appManifest.json
new file mode 100644
index 0000000..e039f5b
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/appManifest.json
@@ -0,0 +1,4 @@
+{
+ "referenceId": "createAWorkEventWithAgent",
+ "name": "Create A Work Event With Agent"
+}
diff --git a/catalog/createAWorkEventWithAgent/model/AllWorkEvents.report b/catalog/createAWorkEventWithAgent/model/AllWorkEvents.report
new file mode 100644
index 0000000..5961139
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/AllWorkEvents.report
@@ -0,0 +1,138 @@
+{
+ "id": 1,
+ "name": "AllWorkEvents",
+ "defaultSecurityDomains": [
+ "ManageCreateAnEvent"
+ ],
+ "columns": [
+ {
+ "id": 1,
+ "name": "WorkEvent",
+ "order": "a",
+ "isSelfReferencing": true
+ },
+ {
+ "id": 2,
+ "name": "Event Name",
+ "field": "name",
+ "order": "b"
+ },
+ {
+ "id": 3,
+ "name": "Event Location",
+ "field": "location",
+ "order": "c"
+ },
+ {
+ "id": 4,
+ "name": "Event Description",
+ "field": "description",
+ "order": "d"
+ },
+ {
+ "id": 13,
+ "name": "Start Date",
+ "order": "e",
+ "field": "startDate"
+ },
+ {
+ "id": 15,
+ "name": "End Date",
+ "order": "g",
+ "field": "endDate"
+ },
+ {
+ "id": 5,
+ "name": "Sponsor",
+ "field": "sponsor",
+ "order": "h"
+ },
+ {
+ "id": 7,
+ "name": "Contact Info",
+ "field": "contactInfo",
+ "order": "i"
+ },
+ {
+ "id": 8,
+ "name": "Event Fee",
+ "field": "cost",
+ "order": "j"
+ },
+ {
+ "id": 9,
+ "name": "Created By",
+ "field": "createdBy",
+ "order": "k"
+ },
+ {
+ "id": 10,
+ "name": "Internal Only Event",
+ "field": "internalOnly",
+ "order": "l"
+ },
+ {
+ "id": 11,
+ "name": "Image",
+ "field": "image",
+ "order": "m"
+ },
+ {
+ "id": 12,
+ "name": "Event Venue",
+ "field": "venue",
+ "order": "n"
+ },
+ {
+ "id": 14,
+ "name": "Registrants",
+ "order": "o",
+ "field": "registrants"
+ },
+ {
+ "id": 16,
+ "name": "Long Description",
+ "order": "p",
+ "field": "longDescription"
+ },
+ {
+ "id": 17,
+ "name": "Is Workday Event",
+ "order": "q",
+ "field": "isWorkdayEvent"
+ },
+ {
+ "id": 18,
+ "name": "Prorated Amount",
+ "order": "r",
+ "field": "proratedAmount"
+ },
+ {
+ "id": 19,
+ "name": "Image Uploaded Date",
+ "order": "s",
+ "field": "imageUploadedDate"
+ },
+ {
+ "id": 20,
+ "name": "Requires Approval - Internal Only",
+ "order": "t",
+ "field": "requiresApprovalInternal"
+ },
+ {
+ "id": 21,
+ "name": "Requires Approval",
+ "order": "u",
+ "field": "requiresApproval"
+ }
+ ],
+ "businessObject": "WorkEvent",
+ "sortConfigs": [
+ {
+ "id": 1,
+ "order": "a",
+ "field": "name",
+ "direction": "ASCENDING"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/AttachedFile.attachment b/catalog/createAWorkEventWithAgent/model/AttachedFile.attachment
new file mode 100644
index 0000000..566bcc0
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/AttachedFile.attachment
@@ -0,0 +1,35 @@
+{
+ "id": 2,
+ "name": "AttachedFile",
+ "label": "Attached File",
+ "defaultSecurityDomains": [
+ "ManageCreateAnEvent"
+ ],
+ "defaultCollection": {
+ "name": "attachedFiles",
+ "label": "All Attached Files for Event"
+ },
+ "fields": [
+ {
+ "id": 1,
+ "name": "uploadedBy",
+ "label": "File Uploaded By",
+ "type": "SINGLE_INSTANCE",
+ "target": "WORKER"
+ },
+ {
+ "id": 2,
+ "name": "uploadedDate",
+ "label": "File Upload Date",
+ "type": "DATE",
+ "precision": "DAY"
+ },
+ {
+ "id": 3,
+ "name": "event",
+ "label": "Event for Attachment",
+ "type": "SINGLE_INSTANCE",
+ "target": "WorkEvent"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/CreateAWorkEvent.task b/catalog/createAWorkEventWithAgent/model/CreateAWorkEvent.task
new file mode 100644
index 0000000..0a90e78
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/CreateAWorkEvent.task
@@ -0,0 +1,9 @@
+{
+ "id": 1,
+ "name": "CreateAWorkEvent",
+ "label": "Create A Work Event",
+ "routePath": "/createEventWizard",
+ "securityDomains": [
+ "ManageCreateAnEvent"
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/CreateWorkEvent.businessprocess b/catalog/createAWorkEventWithAgent/model/CreateWorkEvent.businessprocess
new file mode 100644
index 0000000..df8904d
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/CreateWorkEvent.businessprocess
@@ -0,0 +1,30 @@
+{
+ "id": 1,
+ "name": "CreateWorkEvent",
+ "label": "Create Work Event",
+ "description": "Business Process Type for Create Work Event",
+ "targetBusinessObject": "WorkEvent",
+ "allowsCancel": true,
+ "useForOrchestrate": false,
+ "approvalStep": {
+ "actions": [
+ "APPROVE",
+ "DENY",
+ "SEND_BACK"
+ ],
+ "pageRoute": "/approve/{eventId}"
+ },
+ "details": {
+ "pageRoute": "/details/{eventId}"
+ },
+ "revisePageRoute": "/revise/{eventStepId}",
+ "actionSteps": [
+ {
+ "id": 1,
+ "name": "BookEventSpace",
+ "label": "Book Event Space",
+ "description": "Step to review my business process",
+ "pageRoute": "/review/{eventStepId}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/EventImage.attachment b/catalog/createAWorkEventWithAgent/model/EventImage.attachment
new file mode 100644
index 0000000..92e71f5
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/EventImage.attachment
@@ -0,0 +1,28 @@
+{
+ "id": 1,
+ "name": "EventImage",
+ "label": "Event Image",
+ "defaultSecurityDomains": [
+ "ManageCreateAnEvent"
+ ],
+ "defaultCollection": {
+ "name": "eventImages",
+ "label": "All Event Images"
+ },
+ "fields": [
+ {
+ "id": 1,
+ "name": "uploadedBy",
+ "label": "Image Uploaded By",
+ "type": "SINGLE_INSTANCE",
+ "target": "WORKER"
+ },
+ {
+ "id": 2,
+ "name": "uploadedDate",
+ "label": "Image Upload Date",
+ "type": "DATE",
+ "precision": "DAY"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/ManageCreateAnEvent.securitydomain b/catalog/createAWorkEventWithAgent/model/ManageCreateAnEvent.securitydomain
new file mode 100644
index 0000000..470078f
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/ManageCreateAnEvent.securitydomain
@@ -0,0 +1,6 @@
+{
+ "id": 1,
+ "name": "ManageCreateAnEvent",
+ "label": "Manage: Create an Event",
+ "enabledForPageSecurity": true
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/RegisterForEvents.securitydomain b/catalog/createAWorkEventWithAgent/model/RegisterForEvents.securitydomain
new file mode 100644
index 0000000..4968feb
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/RegisterForEvents.securitydomain
@@ -0,0 +1,5 @@
+{
+ "id": 2,
+ "name": "RegisterForEvents",
+ "enabledForPageSecurity": true
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/Registrant.businessobject b/catalog/createAWorkEventWithAgent/model/Registrant.businessobject
new file mode 100644
index 0000000..2035e27
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/Registrant.businessobject
@@ -0,0 +1,34 @@
+{
+ "id": 2,
+ "name": "Registrant",
+ "label": "Registrant",
+ "defaultSecurityDomains": [
+ "RegisterForEvents",
+ "ManageCreateAnEvent"
+ ],
+ "defaultCollection": {
+ "name": "registrants",
+ "label": "Registrants",
+ "description": "Displays all registrants within the Create a Work Event Extend app"
+ },
+ "fields": [
+ {
+ "label": "Worker",
+ "secureByTarget": false,
+ "target": "WORKER",
+ "useForDisplay": true,
+ "type": "SINGLE_INSTANCE",
+ "name": "worker",
+ "id": 1,
+ "description": "Displays a worker registered for an event"
+ },
+ {
+ "label": "Event",
+ "target": "WorkEvent",
+ "type": "SINGLE_INSTANCE",
+ "name": "event",
+ "description": "Displays a work event",
+ "id": 2
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/UpdateEvent.task b/catalog/createAWorkEventWithAgent/model/UpdateEvent.task
new file mode 100644
index 0000000..027df89
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/UpdateEvent.task
@@ -0,0 +1,11 @@
+{
+ "id": 2,
+ "name": "UpdateEvent",
+ "label": "Update Event",
+ "secured": true,
+ "businessObject": "WorkEvent",
+ "routePath": "updateEvent/{taskObjectId}",
+ "securityDomains": [
+ "ManageCreateAnEvent"
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/WorkEvent.businessobject b/catalog/createAWorkEventWithAgent/model/WorkEvent.businessobject
new file mode 100644
index 0000000..4305822
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/WorkEvent.businessobject
@@ -0,0 +1,167 @@
+{
+ "id": 1,
+ "name": "WorkEvent",
+ "label": "Work Event",
+ "defaultSecurityDomains": [
+ "RegisterForEvents",
+ "ManageCreateAnEvent"
+ ],
+ "defaultCollection": {
+ "name": "workEvents",
+ "label": "All Work Events",
+ "description": "Displays all work events within the Create a Work Event Extend app"
+ },
+ "fields": [
+ {
+ "isReferenceId": true,
+ "useForDisplay": true,
+ "type": "TEXT",
+ "label": "Event Name",
+ "name": "name",
+ "id": 1,
+ "description": "Displays the name of the event"
+ },
+ {
+ "type": "TEXT",
+ "label": "Event Location",
+ "name": "location",
+ "id": 2,
+ "description": "Displays the location of the event"
+ },
+ {
+ "type": "TEXT",
+ "label": "Event Description",
+ "name": "description",
+ "id": 3,
+ "description": "Displays the description of the event"
+ },
+ {
+ "type": "TEXT",
+ "label": "Sponsor",
+ "name": "sponsor",
+ "id": 4,
+ "description": "Displays the sponsor of the event"
+ },
+ {
+ "precision": "DAY",
+ "type": "DATE",
+ "label": "Start Date",
+ "name": "startDate",
+ "id": 5,
+ "description": "Displays the start date of the event"
+ },
+ {
+ "type": "TEXT",
+ "label": "Contact Info",
+ "name": "contactInfo",
+ "id": 6,
+ "description": "Displays the contact info of the event organizer"
+ },
+ {
+ "type": "CURRENCY",
+ "label": "Event Fee",
+ "name": "cost",
+ "id": 7,
+ "description": "Displays the cost of the event"
+ },
+ {
+ "secureByTarget": true,
+ "target": "WORKER",
+ "type": "SINGLE_INSTANCE",
+ "label": "Created By",
+ "name": "createdBy",
+ "id": 8,
+ "description": "Displays name of the event creator"
+ },
+ {
+ "type": "BOOLEAN",
+ "label": "Internal Only Event",
+ "name": "internalOnly",
+ "id": 9,
+ "description": "Displays whether or not the event is an internal event"
+ },
+ {
+ "target": "EventImage",
+ "label": "Image",
+ "type": "SINGLE_INSTANCE",
+ "name": "image",
+ "id": 10,
+ "description": "Displays an image about the event"
+ },
+ {
+ "target": "LOCATION",
+ "label": "Event Venue",
+ "type": "SINGLE_INSTANCE",
+ "name": "venue",
+ "id": 11,
+ "description": "Displays the event venue"
+ },
+ {
+ "target": "Registrant",
+ "type": "MULTI_INSTANCE",
+ "label": "Registrants",
+ "name": "registrants",
+ "id": 12,
+ "description": "Displays the registrants of the event"
+ },
+ {
+ "precision": "DAY",
+ "type": "DATE",
+ "name": "endDate",
+ "id": 13,
+ "description": "Displays the end date of the event"
+ }
+ ],
+ "derivedFields": [
+ {
+ "expression": "'Event: ' + name + ' - ' + description",
+ "label": "Long Description",
+ "type": "TEXT",
+ "name": "longDescription",
+ "id": 1,
+ "description": "Displays long description for the event"
+ },
+ {
+ "expression": "sponsor == 'Workday'",
+ "label": "Is Workday Event",
+ "type": "BOOLEAN",
+ "name": "isWorkdayEvent",
+ "id": 2,
+ "description": "Displays whether or not the event is a Workday Event"
+ },
+ {
+ "expression": "cost * 0.7",
+ "decimals": 2,
+ "label": "Prorated Amount",
+ "type": "DECIMAL",
+ "name": "proratedAmount",
+ "id": 3,
+ "description": "Displays any discount for the event"
+ },
+ {
+ "expression": "image.uploadedDate",
+ "label": "Image Uploaded Date",
+ "precision": "DAY",
+ "type": "DATE",
+ "name": "imageUploadedDate",
+ "id": 4,
+ "description": "Displays the date that the image was uploaded"
+ },
+ {
+ "expression": "(cost > 1000) && internalOnly",
+ "label": "Requires Approval - Internal Only",
+ "type": "BOOLEAN",
+ "name": "requiresApprovalInternal",
+ "id": 5,
+ "description": "Displays internally whether or not approval is required"
+ },
+ {
+ "expression": "(cost > 5000) || requiresApprovalInternal",
+ "label": "Requires Approval",
+ "type": "BOOLEAN",
+ "name": "requiresApproval",
+ "id": 6,
+ "description": "if the cost is above $5000, then approval is required "
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/model/WorkEventRegistration.task b/catalog/createAWorkEventWithAgent/model/WorkEventRegistration.task
new file mode 100644
index 0000000..0e5a6e5
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/model/WorkEventRegistration.task
@@ -0,0 +1,9 @@
+{
+ "id": 3,
+ "name": "WorkEventRegistration",
+ "label": "Work Event Registration",
+ "routePath": "/",
+ "securityDomains": [
+ "RegisterForEvents"
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/calendarWorkEvents.pmd b/catalog/createAWorkEventWithAgent/presentation/calendarWorkEvents.pmd
new file mode 100644
index 0000000..a6ea688
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/calendarWorkEvents.pmd
@@ -0,0 +1,122 @@
+{
+ "id": "calendarWorkEvents",
+ "onLoad": "<%
+ // The sample calendar is view-only for non-employees.
+ me.workerType.descriptor == 'Employee' ? pageVariables.viewOnly = false : pageVariables.viewOnly = true;
+ getWorkEvents.data.forEach(e => { e.status = getBPEvents.data.find(bpEvent => {bpEvent.businessProcessTarget.id == e.id}).status ?? 'Approved'});
+ console.info('augmented work event data is ' + json:asJSON(getWorkEvents.data));
+ %>",
+ "presentation": {
+ "title": {
+ "type": "title",
+ "id": "title",
+ "label": "Work Event Calendar"
+ },
+ "body": {
+ "type": "section",
+ "children": [
+ {
+ "type": "multiSelectCalendar",
+ "calendarStartDate": "<% date:now('America/Los_Angeles') %>",
+ "eventData": {
+ "on": "<% getWorkEvents.data %>",
+ "as": "event",
+ "template": {
+ "startDate": "<% date:parse(event.startDate) %>",
+ "endDate": "<% date:parse(event.endDate ?? event.startDate) %>",
+ "style": "<% if (event.status == 'In Progress') { 'SUBMITTED'; } else
+ if (event.status == 'Successfully Completed' || event.status == 'Approved') { 'APPROVED';} else
+ if (event.status == 'Denied') { 'DENIED'; } %>",
+ "title": "<% event.descriptor %>",
+ "editTask": {
+ "task": {
+ "taskId": "registerForEvent",
+ "parameters": {
+ "workEventId": "<% event.id %>"
+ }
+ },
+ "render": "<% !pageVariables.viewOnly %>"
+ }
+ }
+ },
+ "submitButton": {
+ "render": "<% !pageVariables.viewOnly %>",
+ "label": "<% 'Create Work Event' %>",
+ "datesQueryParamName": "selectedDates",
+ "task": {
+ "taskId": "quickAddWorkEvent",
+ "parameters": {
+ "createdById": "<% me.id %>",
+ "createdByName": "<% me.descriptor %>"
+ }
+ }
+ },
+ "sidebar": {
+ "task": {
+ "taskId": "sidebarInfo",
+ "parameters": {
+ "name": "<% me.descriptor %>",
+ "businessTitle": "<% me.primaryJob.businessTitle %>",
+ "primaryLocation": "<% me.primaryJob.location.descriptor %>"
+ }
+ },
+ "position": "left"
+ }
+ },
+ {
+ "type": "cardContainer",
+ "layout": "MASONRY",
+ "cards": [
+ {
+ "type": "card",
+ "cardId": "CalendarLinks"
+ }
+ ]
+ }
+ ]
+ },
+ "footer": {
+ "type": "footer",
+ "children": [
+ {
+ "type": "richText",
+ "enabled": "false",
+ "value": "Powered By Workday Extend"
+ }
+ ]
+ }
+ },
+ "endPoints": [
+ {
+ "baseUrlType": "workday-staffing",
+ "name": "me",
+ "authType": "sso",
+ "url": "workers/me"
+ },
+ {
+ "name": "getWorkEvents",
+ "baseUrlType": "app",
+ "url": "workEvents?limit=<% pagingVariables.limit ?: 50 %>&offset=<% pagingVariables.offset ?: 0 %>",
+ "authType": "sso",
+ "httpMethod": "GET"
+ },
+ {
+ "baseUrlType": "workday-wql",
+ "name": "getBPEvents",
+ "authType": "sso",
+ "url": "<% //Grab the request ID using the BP Event ID. queryParams.eventId will automatically pull in the BP Event ID if it is in the page's scope
+ var query = 'SELECT businessProcessTarget, status FROM ' + site.applicationId + '_createWorkEventEvents';
+ //if you reuse this code be sure to replace _createWorkEventEvents above with the appropriate endpoint for your Model BP
+ console.info('getBPEvents query is ' + query);
+ return 'data?query=' + query.pathEncode() %>",
+ "failOnStatusCodes": [
+ {
+ "code": "400"
+ },
+ {
+ "code": "403"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/cards/AppLinks.card b/catalog/createAWorkEventWithAgent/presentation/cards/AppLinks.card
new file mode 100644
index 0000000..94711dd
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/cards/AppLinks.card
@@ -0,0 +1,50 @@
+{
+ "id": "AppLinks",
+ "parameters": [
+ ],
+ "header": {
+ "title": "Get Started",
+ "icon": "wd-accent-megaphone",
+ "indicator": {
+ "label": "Welcome!",
+ "color": "blue"
+ }
+ },
+ "body": {
+ "type": "pillCard",
+ "pills": [
+ {
+ "label": "Create an Event",
+ "taskReference": {
+ "taskId": "createEventWizard"
+ }
+ },
+ {
+ "label": "Manage Events",
+ "taskReference": {
+ "taskId": "manageEvents"
+ }
+ },
+ {
+ "label": "Register for an Event",
+ "taskReference": {
+ "taskId": "registerForEvents"
+ }
+ },
+ {
+ "label": "Work Event Calendar",
+ "taskReference": {
+ "taskId": "calendarWorkEvents"
+ }
+ }
+ ]
+ },
+ "footer": [
+ {
+ "label": "Return Home",
+ "taskReference": {
+ "taskId": "eventHub"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/cards/CalendarLinks.card b/catalog/createAWorkEventWithAgent/presentation/cards/CalendarLinks.card
new file mode 100644
index 0000000..6222243
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/cards/CalendarLinks.card
@@ -0,0 +1,43 @@
+{
+ "id": "CalendarLinks",
+ "parameters": [
+ ],
+ "header": {
+ "title": "What's Next?",
+ "icon": "wd-accent-calendar",
+ "indicator": {
+ "color": "blue"
+ }
+ },
+ "body": {
+ "type": "pillCard",
+ "pills": [
+ {
+ "label": "Work Event Calendar",
+ "taskReference": {
+ "taskId": "calendarWorkEvents"
+ }
+ },
+ {
+ "label": "Create a Work Event",
+ "taskReference": {
+ "taskId": "createEventWizard"
+ }
+ },
+ {
+ "label": "Register For Events",
+ "taskReference": {
+ "taskId": "registerForEvents"
+ }
+ }
+ ]
+ },
+ "footer": [
+ {
+ "label": "Return Home",
+ "taskReference": {
+ "taskId": "eventHub"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/cards/EventImage.card b/catalog/createAWorkEventWithAgent/presentation/cards/EventImage.card
new file mode 100644
index 0000000..796e6cd
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/cards/EventImage.card
@@ -0,0 +1,25 @@
+{
+ "id": "EventImage",
+ "parameters": ["title", "subtitle", "imageURL"],
+ "header": {
+ "title": "<% title %>",
+ "subtitle": "<% subtitle %>"
+ },
+
+ "body": {
+ "type": "imageCard",
+ "endPoint": {
+ "name": "imageEndPointName",
+ "url" : "<% imageURL %>",
+ "authType" : "sso"
+ }
+ },
+ "footer": [
+ {
+ "label": "View All Events",
+ "taskReference": {
+ "taskId": "calendarWorkEvents"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/createAWorkEventWithAgent.amd b/catalog/createAWorkEventWithAgent/presentation/createAWorkEventWithAgent.amd
new file mode 100644
index 0000000..a788096
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/createAWorkEventWithAgent.amd
@@ -0,0 +1,313 @@
+{
+ "appProperties": [],
+ "flowDefinitions": [
+ {
+ "id": "calendarPopup",
+ "flowSteps": [
+ {
+ "id": "startCalendarEventPopupMicro",
+ "transitions": [
+ {
+ "order": "a",
+ "value": "concludeCalendarEventPopupMicro",
+ "condition": "true"
+ }
+ ],
+ "startsFlow": true,
+ "taskId": "registerForEvent"
+ },
+ {
+ "id": "concludeCalendarEventPopupMicro",
+ "endsFlow": true,
+ "taskId": "microConfirm"
+ }
+ ]
+ },
+ {
+ "id": "calendarSubmitMicroFlow",
+ "flowSteps": [
+ {
+ "id": "startCalendarSubmitMicro",
+ "transitions": [
+ {
+ "order": "a",
+ "value": "concludeCalendarSubmitMicro",
+ "condition": "true"
+ }
+ ],
+ "startsFlow": true,
+ "taskId": "quickAddWorkEvent"
+ },
+ {
+ "id": "concludeCalendarSubmitMicro",
+ "endsFlow": true,
+ "taskId": "microConfirm"
+ }
+ ]
+ },
+ {
+ "id": "actionStepFlow",
+ "flowSteps": [
+ {
+ "id": "actionStep",
+ "transitions": [
+ {
+ "id": "submitted",
+ "order": "a",
+ "value": "actionConfirm",
+ "condition": "true"
+ }
+ ],
+ "startsFlow": true,
+ "taskId": "workEventActionStep"
+ },
+ {
+ "id": "actionConfirm",
+ "endsFlow": true,
+ "taskId": "microConfirm"
+ }
+ ]
+ },
+ {
+ "id": "confirmationFlow",
+ "flowSteps": [
+ {
+ "id": "home",
+ "transitions": [
+ {
+ "order": "a",
+ "value": "registerForEvent",
+ "condition": "true"
+ }
+ ],
+ "taskId": "registerForEvents"
+ },
+ {
+ "id": "registerForEvent",
+ "transitions": [
+ {
+ "order": "b",
+ "value": "registerConfirmation",
+ "condition": "true"
+ }
+ ],
+ "taskId": "registerForEvent",
+ "name": "registerForEvent"
+ },
+ {
+ "id": "registerConfirmation",
+ "endsFlow": true,
+ "taskId": "registerConfirmation",
+ "name": "registerConfirmation"
+ }
+ ]
+ },
+ {
+ "id": "orderSubmitConcludeFlow",
+ "flowSteps": [
+ {
+ "id": "start",
+ "transitions": [
+ {
+ "order": "a",
+ "value": "deleteAttachmentFlowStep",
+ "condition": "<% flowVariables.deleteClicked %>"
+ }
+ ],
+ "startsFlow": true,
+ "taskId": "updateEvent"
+ },
+ {
+ "id": "deleteAttachmentFlowStep",
+ "transitions": [
+ {
+ "order": "a",
+ "value": "updateEventFlowStep",
+ "condition": "<% flowVariables.deleteClicked %>"
+ }
+ ],
+ "taskId": "deleteAttachments"
+ },
+ {
+ "id": "updateEventFlowStep",
+ "endsFlow": true,
+ "taskId": "updateEvent"
+ }
+ ],
+ "cancelOutOfFlow": true
+ }
+ ],
+ "tasks": [
+ {
+ "id": "home",
+ "routingPattern": "/home",
+ "page": {
+ "id": "home"
+ }
+ },
+ {
+ "id": "manageEvents",
+ "routingPattern": "manageEvents",
+ "page": {
+ "id": "manageEvents"
+ }
+ },
+ {
+ "id": "updateEvent",
+ "routingPattern": "updateEvent/{workEventId}",
+ "page": {
+ "id": "updateEvent"
+ }
+ },
+ {
+ "id": "updateEventImage",
+ "routingPattern": "updateEventImage",
+ "page": {
+ "id": "updateEventImage"
+ }
+ },
+ {
+ "id": "viewRequestByEventID",
+ "routingPattern": "/details/{eventId}",
+ "page": {
+ "id": "viewRequestByEventID"
+ }
+ },
+ {
+ "id": "workEventApprove",
+ "routingPattern": "/approve/{eventId}",
+ "page": {
+ "id": "workEventApprove"
+ }
+ },
+ {
+ "id": "workEventRevise",
+ "routingPattern": "/revise/{eventStepId}",
+ "page": {
+ "id": "workEventRevise"
+ }
+ },
+ {
+ "id": "workEventActionStep",
+ "routingPattern": "/review/{eventStepId}",
+ "page": {
+ "id": "workEventActionStep"
+ }
+ },
+ {
+ "id": "microConfirm",
+ "routingPattern": "/microConfirm",
+ "page": {
+ "id": "microConfirm"
+ }
+ },
+ {
+ "id": "eventHub",
+ "routingPattern": "/",
+ "page": {
+ "id": "eventHub"
+ }
+ },
+ {
+ "id": "manageMyEvents",
+ "routingPattern": "/manageMyEvents",
+ "page": {
+ "id": "manageMyEvents"
+ }
+ },
+ {
+ "id": "createEventWizard",
+ "routingPattern": "/createEventWizard",
+ "page": {
+ "id": "createEventWizard"
+ }
+ },
+ {
+ "id": "registerForEvents",
+ "routingPattern": "/registerForEvents",
+ "page": {
+ "id": "registerForEvents"
+ }
+ },
+ {
+ "id": "registerForEvent",
+ "routingPattern": "/registerForEvent",
+ "page": {
+ "id": "registerForEvent"
+ }
+ },
+ {
+ "id": "registerConfirmation",
+ "routingPattern": "/registerConfirmation",
+ "page": {
+ "id": "registerConfirmation"
+ }
+ },
+ {
+ "id": "calendarWorkEvents",
+ "routingPattern": "/calendarWorkEvents",
+ "page": {
+ "id": "calendarWorkEvents"
+ }
+ },
+ {
+ "id": "sidebarInfo",
+ "routingPattern": "/sidebarInfo",
+ "page": {
+ "id": "sidebarInfo"
+ }
+ },
+ {
+ "id": "quickAddWorkEvent",
+ "routingPattern": "/quickAddWorkEvent",
+ "page": {
+ "id": "quickAddWorkEvent"
+ }
+ },
+ {
+ "id": "deleteAttachments",
+ "routingPattern": "/deleteAttachments",
+ "page": {
+ "id": "deleteAttachments"
+ }
+ },
+ {
+ "id": "createEvent",
+ "routingPattern": "/createEvent",
+ "page": {
+ "id": "createEvent"
+ }
+ }
+ ],
+ "applicationId": "createAWorkEventWithAgent",
+ "dataProviders": [
+ {
+ "key": "app",
+ "value": "<% apiGatewayEndpoint + '/apps/' + site.applicationId + '/v1/' %>"
+ },
+ {
+ "key": "workday-bp",
+ "value": "<% apiGatewayEndpoint + '/businessProcess/v1/' %>"
+ },
+ {
+ "key": "workday-common",
+ "value": "<% apiGatewayEndpoint + '/common/v1/' %>"
+ },
+ {
+ "key": "workday-staffing",
+ "value": "<% apiGatewayEndpoint + '/staffing/v1/' %>"
+ },
+ {
+ "key": "workday-orchestration",
+ "value": "<% apiGatewayEndpoint + '/orchestrate/v1/apps/' + site.applicationId + '/orchestrations/' %>"
+ },
+ {
+ "key": "graph",
+ "value": "<% apiGatewayEndpoint + '/graphql/v1/' %>"
+ },
+ {
+ "key": "workday-wql",
+ "value": "<% apiGatewayEndpoint + '/wql/v1/' %>"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/createAWorkEventWithAgent.smd b/catalog/createAWorkEventWithAgent/presentation/createAWorkEventWithAgent.smd
new file mode 100644
index 0000000..2fba6a2
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/createAWorkEventWithAgent.smd
@@ -0,0 +1,21 @@
+{
+ "id": "createAWorkEventWithAgent",
+ "languages": [
+ {
+ "code": "en-US",
+ "description": "English"
+ }
+ ],
+ "siteAuth": {
+ "authTypes": [
+ {
+ "scheme": "SSO",
+ "id": "sso"
+ }
+ ]
+ },
+ "siteId": "createAWorkEventWithAgent",
+ "cdnEnabled": true,
+ "siteProperties": [ ],
+ "applicationId": "createAWorkEventWithAgent"
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/createEvent.pmd b/catalog/createAWorkEventWithAgent/presentation/createEvent.pmd
new file mode 100644
index 0000000..2129e53
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/createEvent.pmd
@@ -0,0 +1,235 @@
+{
+ "id": "createEvent",
+ "securityDomains": [
+
+ ],
+ "endPoints": [
+ {
+ "name": "me",
+ "baseUrlType": "workday-staffing",
+ "url": "workers/me",
+ "authType": "sso"
+ }
+ ],
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "createEvent",
+ "baseUrlType": "app",
+ "url": "workEvents",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "onSend": "<% //Outbound endpoint that submits the BO
+ //self.data is the payload as it exists coming into this onSend event
+ self.data.put('createdBy',{'id':me.id}); //add createdBy object
+ self.data.internalOnly = internalOnlyRadioGroup.value[0] == 'yes' ? true : false; //convert internalOnly radio group response from String to Boolean
+ console.info('new event payload is ' + json:asJSON(self.data)); return self.data; %>"
+ },
+ {
+ "name": "createWorkEventEvents",
+ "baseUrlType": "app",
+ "url": "createWorkEventEvents",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "onSend": "<%
+ //Kicks off BP. Pass in ID of the Work Event business object and when it is successfully created will trigger BP. Name element is the name of the BP in lower camel case + 'Events' from the .businessprocess file
+ console.info(json:asJSON(self.data));
+ return self.data;
+ %>",
+ "values": [
+ {
+ "outboundPath": "businessProcessTarget.id",
+ "value": "<% //value must match name of the outbound endpoint that submits the BO, here 'createEvent' and 'id' is the WID of that BO
+ createEvent.id %>"
+ }
+ ]
+ },
+ {
+ "name": "eventAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFilesCollection' //using the Collection endpoint to let us use onMultiPartSend() and to upload multiple files at once
+ %>",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "exclude": "<% empty(attachmentFileUploader.children) %>",
+ "onMultiPartSend": "<%
+ for (var file : self.data.metadata.attachedFiles) { //loop through as many files as were uploaded
+ //Put an uploadedBy object with an id field to the JSON part.
+ file.put('uploadedBy',{'id': me.id}); //put the new uploadedBy map in the metadata. This captures the WID of the uploader.
+ //Put uploadedDate field to the JSON part.
+ file.uploadedDate = date:getTodaysDate(date:getDateTimeZone(userTimeZone)); //put the uploadedDate field in the metadata
+ file.put('event',{'id': createEvent.id}); //link the file with the work event
+ console.info('metadata is ' + json:asJSON(self.data)); //log the metadata being passed in for debugging purposes
+ }
+ self.data;
+ %>"
+ }
+ ]
+ },
+ "presentation": {
+ "headerSize": "VPS_DEFAULT",
+ "pageType": "edit",
+ "title": {
+ "type": "title",
+ "label": "Create a Work Event",
+ "enabled": false
+ },
+ "body": {
+ "type": "section",
+ "horizontal": true,
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Name of Event",
+ "required": "<% true %>",
+ "valueOutBinding": "createEvent.name"
+ },
+ {
+ "type": "text",
+ "id": "location",
+ "label": "Proposed Event Location (free text)",
+ "required": "<% true %>",
+ "valueOutBinding": "createEvent.location"
+ },
+ {
+ "type": "richText",
+ "id": "description",
+ "label": "Description",
+ "required": "<% true %>",
+ "valueOutBinding": "createEvent.description",
+ "validations": [
+ {
+ "regex": "^.{0,1000}",
+ "errorMessage": "Maximum character count (including markup) reached: 1000"
+ }
+ ],
+ "aiPrompt": {
+ "buttons": [
+ {
+ "label": "Generate Work Event Description",
+ "prompt": "<% 'Create a description around 150 characters for' + name.value + ' and include event location ' + location.value %>",
+ "dependencyTags": [
+ "name",
+ "location"
+ ]
+ },
+ {
+ "label": "Refine My Event Description",
+ "prompt": "<% 'Summarize or expand following text to 100 words: ' + description.value %>",
+ "dependencyTags": [
+ "description"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "type": "text",
+ "id": "sponsor",
+ "label": "Event Sponsor",
+ "valueOutBinding": "createEvent.sponsor",
+ "guide": {
+ "type": "guide",
+ "id": "guideName",
+ "title": "Sponsor Name",
+ "text": " Enter the Event Sponsors name:"
+ }
+ },
+ {
+ "type": "date",
+ "id": "startdate",
+ "label": "Start Date of Event",
+ "valueOutBinding": "createEvent.startDate",
+ "required": true
+ },
+ {
+ "type": "date",
+ "id": "enddate",
+ "label": "End Date of Event",
+ "valueOutBinding": "createEvent.endDate",
+ "required": true
+ },
+ {
+ "type": "text",
+ "id": "contactInfo",
+ "label": "Contact Info/Email",
+ "valueOutBinding": "createEvent.contactInfo"
+ },
+ {
+ "type": "currency",
+ "currencyCode": "USD",
+ "id": "cost",
+ "label": "Fee, if any (USD)",
+ "fractionDigits": 2,
+ "valuesOut": [
+ {
+ "value": "USD",
+ "valueOutBinding": "createEvent.cost.currency"
+ },
+ {
+ "value": "<% cost.value %>",
+ "valueOutBinding": "createEvent.cost.value"
+ }
+ ]
+ },
+ {
+ "type": "radioGroup",
+ "id": "internalOnlyRadioGroup",
+ "label": "Is this an internal only event?",
+ "hideDisplayOption": "true",
+ "required": "true",
+ "selectedValues": "<% ! empty internalOnlyRadioGroup.value ? (internalOnlyRadioGroup.value ? ['yes'] : ['no'] ) : ['no'] %>",
+ "instanceList": [
+ {
+ "id": "yes",
+ "descriptor": "Yes"
+ },
+ {
+ "id": "no",
+ "descriptor": "No"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "fileUploader",
+ "label": "Attach Files Related to the Event",
+ "id": "attachmentFileUploader",
+ "maxImageDimension": "1024",
+ "maxFileSize": "5242880",
+ "compact": "<% true %>",
+ "required": "<% false %>",
+ "fileTypes": "*",
+ "valueOutBinding": "<% 'eventAttachments:attachedFiles' //outbound endpoint name : attachment obect API URL
+ %> ",
+ "metadata": "<%
+ {:} //need at least an empty map in a metadata tag for onMultiPartSend() to have metadata to work with!
+ %>"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "footer": {
+ "type": "footer",
+ "children": [
+ {
+ "type": "richText",
+ "enabled": false,
+ "value": "Powered By
Workday Extend 🎟️"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/createEventWizard.pmd b/catalog/createAWorkEventWithAgent/presentation/createEventWizard.pmd
new file mode 100644
index 0000000..922743d
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/createEventWizard.pmd
@@ -0,0 +1,32 @@
+{
+ "id": "createEventWizard",
+ "endPoints": [ ],
+ "presentation": {
+ "body": {
+ "type": "editWizard",
+ "title": "Create Work Event",
+ "subtitle": "New Event",
+ "summarySettings": {
+ "enabled": true,
+ "label": "Review Event"
+ },
+ "editTasks": [
+ {
+ "label": "Create Event",
+ "task": {
+ "taskId": "createEvent"
+ }
+ }
+ ],
+ "conclusion": {
+ "task": {
+ "taskId": "eventHub",
+ "parameters": {
+ "message": "Request has been submitted."
+ }
+ },
+ "render": true
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/deleteAttachments.pmd b/catalog/createAWorkEventWithAgent/presentation/deleteAttachments.pmd
new file mode 100644
index 0000000..0e2fa61
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/deleteAttachments.pmd
@@ -0,0 +1,119 @@
+{
+ "id": "deleteAttachments",
+ "include": [
+ "fileTypes.script"
+ ],
+ "endPoints": [
+ {
+ "name": "getAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFiles?event=' + flowVariables.currentEvent //'instance filter' checks for files that match the given Model BO
+ %>",
+ "authType": "sso"
+ }
+ ],
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "deleteAttachment",
+ "url": "attachedFiles/<% fileId.value %>",
+ "authType": "sso",
+ "baseUrlType": "app",
+ "httpMethod": "DELETE",
+ "failOnStatusCodes": [
+ "400",
+ "403"
+ ],
+ "exclude": "<%!fileSelector.value%>"
+ }
+ ],
+ "responseErrorDetail": {
+ "errorSummary": "<%error%>",
+ "errors": "<%list:mapAttribute(errors, 'error')%>"
+ }
+ },
+ "presentation": {
+ "headerSize": "STANDARD",
+ "title": {
+ "type": "title",
+ "label": "<% 'Delete Documents via Selections (File download support)' %>"
+ },
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "readOnlyText",
+ "label": "Information",
+ "value": "This page executes delete requests sequentially."
+ },
+ {
+ "type": "grid",
+ "id": "gridAttachments",
+ "rows": "<% getAttachments.data %>",
+ "doNotAdd": true,
+ "doNotRemove": true,
+ "rowVariableName": "file",
+ "columns": [
+ {
+ "type": "column",
+ "id": "col0",
+ "columnId": "col0",
+ "cellTemplate": {
+ "type": "checkBox",
+ "id": "fileSelector",
+ "value": "<% false %>"
+ },
+ "label": "Select"
+ },
+ {
+ "type": "column",
+ "id": "col1",
+ "columnId": "col1",
+ "cellTemplate": {
+ "type": "section",
+ "children": [
+ {
+ "type": "fileUploader",
+ "id": "multiFileUploader",
+ "enabled": "false",
+ "singular": false,
+ "children": [
+ {
+ "type": "fileUploaderRow",
+ "render": "<% !(empty file.fileName) %>",
+ "attachment": {
+ "type": "attachmentList",
+ "attachmentURL": "https://api.workday.com/apps/<% site.applicationId %>/v1/attachedFiles/<% file.id %>",
+ "attachmentAuth": "sso",
+ "attachmentName": "<% file.fileName %>",
+ "attachmentUriTemplate": "[Target]",
+ "instanceList": [
+ {
+ "action": "attachment",
+ "descriptor": "<% file.fileName%>"
+ }
+ ]
+ },
+ "fileName": "<% file.fileName%>",
+ "fileType": "<% fileTypes.getMimeTypeIDbyContentType(file.contentType.id) %>"
+ }
+ ],
+ "fileUploaderRowTemplate": [ ]
+ },
+ {
+ "type": "hidden",
+ "id": "fileId",
+ "value": "<% file.id %>",
+ "valueOutBinding": "deleteAttachment.id"
+ }
+ ]
+ },
+ "label": "File Name"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/eventHub.pmd b/catalog/createAWorkEventWithAgent/presentation/eventHub.pmd
new file mode 100644
index 0000000..d615a9d
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/eventHub.pmd
@@ -0,0 +1,73 @@
+{
+ "id": "eventHub",
+ "securityDomains": [
+ "RegisterForEvents"
+ ],
+ "presentation": {
+ "body": {
+ "type": "hub",
+ "label": "Work Event Registration",
+ "initialTask": {
+ "taskId": "home"
+ },
+ "navigationTasks": [
+ {
+ "type": "item",
+ "icon": "wd-icon-timeline-milestone",
+ "label": "About",
+ "task": {
+ "taskId": "home"
+ }
+ },
+ {
+ "type": "item",
+ "icon": "wd-icon-search-user",
+ "label": "Register For Events",
+ "task": {
+ "taskId": "registerForEvents"
+ }
+ },
+ {
+ "type": "item",
+ "icon": "wd-icon-checkboxes",
+ "label": "My Registered Events",
+ "task": {
+ "taskId": "manageMyEvents"
+ }
+ },
+ {
+ "type": "item",
+ "icon": "wd-icon-time-off-balance",
+ "label": "Manage Events",
+ "task": {
+ "taskId": "manageEvents"
+ }
+ }
+ ],
+ "additionalLinks": {
+ "title": "Create New Work Events",
+ "links": [
+ {
+ "type": "task",
+ "label": "Create Event",
+ "task": {
+ "taskId": "createEventWizard"
+ }
+ },
+ {
+ "type": "task",
+ "label": "Work Event Calendar",
+ "task": {
+ "taskId": "calendarWorkEvents"
+ }
+ },
+ {
+ "type": "external",
+ "label": "Developer Forum",
+ "url": "https://forum.developer.workday.com/"
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/graphQueries/createRegistrant.graphquery b/catalog/createAWorkEventWithAgent/presentation/graphQueries/createRegistrant.graphquery
new file mode 100644
index 0000000..4525ed2
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/graphQueries/createRegistrant.graphquery
@@ -0,0 +1,43 @@
+{
+ "id": "createRegistrant",
+ "parameters": ["worker", "event"],
+ "query": "
+ mutation createAWorkEventMultiinstance_nkzjqw_createRegistrant($createRegistrant_input: CreateAWorkEventMultiinstance_nkzjqw_RegistrantsSummary_CreateInput!) {
+ createAWorkEventMultiinstance_nkzjqw_createRegistrant(
+ input: $createRegistrant_input
+ ) {
+ descriptor
+ event {
+ descriptor
+ workdayID {
+ id
+ }
+ }
+ workdayID {
+ id
+ }
+ worker {
+ descriptor
+ workdayID {
+ id
+ }
+ }
+ }
+}",
+ "variables": {
+ "createRegistrant_input": {
+ "event": {
+ "id": {
+ "id": "<% event %>",
+ "type": "WID"
+ }
+ },
+ "worker": {
+ "id": {
+ "id": "<% worker %>",
+ "type": "WID"
+ }
+ }
+ }
+}
+}
diff --git a/catalog/createAWorkEventWithAgent/presentation/graphQueries/me.graphquery b/catalog/createAWorkEventWithAgent/presentation/graphQueries/me.graphquery
new file mode 100644
index 0000000..c0fd460
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/graphQueries/me.graphquery
@@ -0,0 +1,4 @@
+{
+ "id": "me",
+ "query" : "query { worker(dataSource:{ processingWorker:{}}) { data {descriptor workdayID {id} primaryJob { location { workdayID { id}}} }}}"
+}
diff --git a/catalog/createAWorkEventWithAgent/presentation/graphQueries/updateWorkEventRegistrants.graphquery b/catalog/createAWorkEventWithAgent/presentation/graphQueries/updateWorkEventRegistrants.graphquery
new file mode 100644
index 0000000..c844aac
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/graphQueries/updateWorkEventRegistrants.graphquery
@@ -0,0 +1,30 @@
+{
+ "id": "updateWorkEventRegistrants",
+ "parameters": ["registrants", "event"],
+ "query": "mutation createAWorkEventMultiinstance_nkzjqw_updateWorkEvent($updateWorkEvent_input: CreateAWorkEventMultiinstance_nkzjqw_WorkEventsSummary_UpdateInput!, $updateWorkEvent_workEventsId: IdentifierInput!) {
+ createAWorkEventMultiinstance_nkzjqw_updateWorkEvent(
+ input: $updateWorkEvent_input
+ workEventsId: $updateWorkEvent_workEventsId
+ ) {
+ registrants {
+ descriptor
+ workdayID {
+ id
+ }
+ }
+ workdayID {
+ id
+ }
+ }
+}
+ ",
+ "variables": {
+ "updateWorkEvent_input": {
+ "registrants": "<% registrants %>"
+ },
+ "updateWorkEvent_workEventsId": {
+ "id": "<% event %>",
+ "type": "WID"
+ }
+ }
+}
diff --git a/catalog/createAWorkEventWithAgent/presentation/home.pmd b/catalog/createAWorkEventWithAgent/presentation/home.pmd
new file mode 100644
index 0000000..65683a2
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/home.pmd
@@ -0,0 +1,76 @@
+{
+ "id": "home",
+ "securityDomains": [
+ "RegisterForEvents"
+ ],
+ "endPoints": [ ],
+ "presentation": {
+ "headerSize": "VPS_DEFAULT",
+ "title": {
+ "type": "title",
+ "label": "Work Event Registration Home"
+ },
+ "body": {
+ "type": "section",
+ "children": [
+ {
+ "type": "image",
+ "imageUri": "https://upload.wikimedia.org/wikipedia/commons/8/80/Workday_logo.svg",
+ "sizingHint": "THUMBNAIL",
+ "defaultPhoto": false,
+ "userPhoto": false
+ },
+ {
+ "type": "richText",
+ "label": "",
+ "id": "richTextId4",
+ "value": "
View Work Events or use the Create an Event button
to add a new Work Event.
"
+ },
+ {
+ "type": "richText",
+ "id": "blurb",
+ "label": "Why it Matters",
+ "value": "- It can boost productivity and creativity. When people are happy and relaxed, they are more likely to be productive and creative. Studies have shown that laughter can actually increase productivity by up to 12%.
+- It can help to reduce stress and improve morale. A stressful work environment can lead to burnout, absenteeism, and turnover. Fun events can help to reduce stress and improve morale, which can lead to a more productive and positive work environment.
+- It can help to build relationships and teamwork. Fun events give people a chance to get to know each other outside of work, which can help to build relationships and teamwork. This can be especially important in workplaces where people are siloed or don't have a lot of opportunities to interact with each other.
+- It can help to attract and retain top talent. In today's competitive job market, companies are looking for ways to attract and retain top talent. Fun events can show potential employees that a company is a fun and supportive place to work, which can make them more likely to want to join the team.
+
+Here are some ideas for fun work events that you can organize:
+
+- Casual Fridays. Allow employees to dress down on Fridays. This can help to create a more relaxed and fun atmosphere in the workplace.
+- Happy hours. Host a happy hour after work for employees to socialize and let loose.
+- Team-building events. Organize team-building events that allow employees to work together and have fun. This could include anything from playing games to going on a scavenger hunt.
+- Trivia nights. Host a trivia night to test employees' knowledge on a variety of topics. This is a great way to get people talking and laughing.
+- Potluck lunches. Have employees bring in their favorite dishes for a potluck lunch. This is a great way to get to know your coworkers better and try new foods.
+
No matter what kind of fun event you organize, make sure that it is something that your employees will enjoy. The goal is to create a fun and relaxed atmosphere that will help to boost productivity, morale, and relationships."
+ },
+ {
+ "type": "section",
+ "title": "Ready to Get Started?",
+ "children": [
+ {
+ "type": "cardContainer",
+ "layout": "MASONRY",
+ "cards": [
+ {
+ "type": "card",
+ "cardId": "AppLinks"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "footer": {
+ "type": "footer",
+ "children": [
+ {
+ "type": "richText",
+ "enabled": false,
+ "value": "Powered By
Workday Extend 🎟️"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/manageEvents.pmd b/catalog/createAWorkEventWithAgent/presentation/manageEvents.pmd
new file mode 100644
index 0000000..53c7362
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/manageEvents.pmd
@@ -0,0 +1,223 @@
+{
+ "id": "manageEvents",
+ "endPoints": [
+ {
+ "name": "events",
+ "baseUrlType": "workday-wql",
+ "url": "<% const query = 'SELECT name, cost, workdayID AS id, description, sponsor, location, startDate, endDate, contactInfo, venue, createdBy, registrants FROM ' + site.applicationId + '_workEvents';
+ console.info('events query is ' + query);
+ return 'data?query=' + query.urlEncode();
+ %>",
+ "authType": "sso"
+ }
+ ],
+ "presentation": {
+ "title": {
+ "type": "title",
+ "label": "Manage Work Events"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "section",
+ "horizontal": true,
+ "children": [
+ {
+ "type": "cardContainer",
+ "layout": "MASONRY",
+ "cards": [
+ {
+ "type": "card",
+ "cardId": "CalendarLinks"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "eventsGrid",
+ "label": "Events",
+ "type": "grid",
+ "rows": "<% events.data %>",
+ "rowVariableName": "event",
+ "columns": [
+ {
+ "cellTemplate": {
+ "type": "button",
+ "label": "<% event.name %>",
+ "action": "LINK",
+ "value": "<% event.id %>",
+ "taskReference": {
+ "taskId": "updateEvent",
+ "parameterBindings": {
+ "workEventId": "<% /* this workEventId is a parameter we are passing to the update page once the user clicks to it.
+ taskId points to the target page, and on that page, the valid queryParam name in the inbond endpoint for the Event BO instance to update is populated here (event.id):*/
+ event.id %>"
+ }
+ }
+ },
+ "label": "Name of Event",
+ "type": "column",
+ "columnId": "name",
+ "sortableAndFilterable": true
+ },
+ {
+ "cellTemplate": {
+ "type": "currency",
+ "label": "Amount (USD)",
+ "id": "cost",
+ "enabled": false,
+ "currencyCode": "USD",
+ "value": "<% event.cost.value ?: 0 %>",
+ "showCurrencySymbol": true,
+ "render": true,
+ "visible": true
+ },
+ "label": "Event Fee",
+ "columnId": "Cost",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "richText",
+ "value": "<% event.description %>"
+ },
+ "label": "Description",
+ "columnId": "Description",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% event.sponsor %>"
+ },
+ "label": "Event Sponsor",
+ "sortableAndFilterable": true,
+ "columnId": "Sponsor",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% event.location %>"
+ },
+ "label": "Location",
+ "columnId": "Proposed Location",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "date",
+ "datePrecision": "DAY",
+ "dateFormat": "yyyy-MM-dd",
+ "value": "<% event.startDate %>"
+ },
+ "label": "Start Date of Event",
+ "sortableAndFilterable": true,
+ "columnId": "StartDateOfEvent",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "date",
+ "datePrecision": "DAY",
+ "dateFormat": "yyyy-MM-dd",
+ "value": "<% event.endDate %>"
+ },
+ "label": "End Date of Event",
+ "sortableAndFilterable": true,
+ "columnId": "EndDateOfEvent",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% event.contactInfo %>"
+ },
+ "label": "Contact Info",
+ "columnId": "ContactInfo",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "instanceList",
+ "instanceList": [
+ {
+ "id": "<% event.venue %>",
+ "descriptor": "<% event.venue.descriptor ?: '' %>"
+ }
+ ]
+ },
+ "label": "Booked Venue",
+ "columnId": "BookedVenue",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "readOnlyText",
+ "value": "<% event.internalOnly%>"
+ },
+ "label": "Internal Only?",
+ "columnId": "InternalOnly",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "instanceList",
+ "instanceList": [
+ {
+ "id": "<% event.createdBy %>",
+ "descriptor": "<% //here we use a ternary or Elvis expression to tell the page to check
+ // whether the descriptor is non-null. If it is null, display an empty string. Else, it will display the expression before the operator.
+ event.createdBy.descriptor ?: '' %>"
+ }
+ ]
+ },
+ "label": "Created By",
+ "columnId": "CreatedBy",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "section",
+ "children": [
+ {
+ "type": "button",
+ "action": "AUXILIARY",
+ "label": "Update",
+ "taskReference": {
+ "taskId": "updateEvent",
+ "parameterBindings": {
+ "workEventId": "<% /* this workEventId is a parameter we are passing to the update page once the user clicks to it.
+ taskId points to the target page, and on that page, the valid queryParam name in the inbond endpoint for the Event BO instance to update is populated here (event.id):*/
+ event.id %>"
+ }
+ }
+ },
+ {
+ "type": "button",
+ "action": "AUXILIARY",
+ "label": "Update Image",
+ "taskReference": {
+ "taskId": "updateEventImage",
+ "parameterBindings": {
+ "workEventId": "<% event.id %>"
+ }
+ }
+ }
+ ]
+ },
+ "columnId": "actions",
+ "type": "column",
+ "label": "Actions"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/manageMyEvents.pmd b/catalog/createAWorkEventWithAgent/presentation/manageMyEvents.pmd
new file mode 100644
index 0000000..0d4b6ba
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/manageMyEvents.pmd
@@ -0,0 +1,232 @@
+{
+ "id": "manageMyEvents",
+ "securityDomains": [
+ "RegisterForEvents"
+ ],
+ "endPoints": [
+ {
+ "name": "me",
+ "baseUrlType": "workday-staffing",
+ "url": "workers/me",
+ "authType": "sso"
+ },
+ {
+ "name": "events",
+ "baseUrlType": "workday-wql",
+ "url": "<% const query = 'SELECT worker, event{workdayID as id, name, cost, description, sponsor, location, startDate, endDate, contactInfo, venue, createdBy} as event FROM ' + site.applicationId + '_registrants WHERE worker in (' + me.id + ')';
+ console.info('events query is ' + query);
+ return 'data?query=' + query.urlEncode();
+ %>",
+ "authType": "sso"
+ }
+ ],
+ "presentation": {
+ "title": {
+ "type": "title",
+ "label": "My Registered Events"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "section",
+ "horizontal": true,
+ "children": [
+ {
+ "type": "cardContainer",
+ "layout": "MASONRY",
+ "cards": [
+ {
+ "type": "card",
+ "cardId": "CalendarLinks"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "eventsGrid",
+ "label": "Events I Am Registered For",
+ "type": "grid",
+ "rows": "<% events.data; %>",
+ "rowVariableName": "this",
+ "columns": [
+ {
+ "cellTemplate": {
+ "type": "button",
+ "label": "<% this.event.name %>",
+ "action": "LINK",
+ "value": "<% this.event.id %>",
+ "taskReference": {
+ "taskId": "updateEvent",
+ "parameterBindings": {
+ "workEventId": "<% /* this workEventId is a parameter we are passing to the update page once the user clicks to it.
+ taskId points to the target page, and on that page, the valid queryParam name in the inbond endpoint for the Event BO instance to update is populated here (event.id):*/
+ this.event.id %>"
+ }
+ }
+ },
+ "label": "Name of Event",
+ "type": "column",
+ "columnId": "name",
+ "sortableAndFilterable": true
+ },
+ {
+ "cellTemplate": {
+ "type": "currency",
+ "label": "Amount (USD)",
+ "id": "cost",
+ "enabled": false,
+ "currencyCode": "USD",
+ "value": "<% this.event.cost.value ?: 0 %>",
+ "showCurrencySymbol": true,
+ "render": true,
+ "visible": true
+ },
+ "label": "Event Fee",
+ "columnId": "Cost",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "richText",
+ "value": "<% this.event.description %>"
+ },
+ "label": "Description",
+ "columnId": "Description",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% this.event.sponsor %>"
+ },
+ "label": "Event Sponsor",
+ "sortableAndFilterable": true,
+ "columnId": "Sponsor",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% this.event.location %>"
+ },
+ "label": "Location",
+ "columnId": "Proposed Location",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "date",
+ "datePrecision": "DAY",
+ "dateFormat": "yyyy-MM-dd",
+ "value": "<% this.event.startDate %>"
+ },
+ "label": "Start Date of Event",
+ "sortableAndFilterable": true,
+ "columnId": "StartDateOfEvent",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "date",
+ "datePrecision": "DAY",
+ "dateFormat": "yyyy-MM-dd",
+ "value": "<% this.event.endDate %>"
+ },
+ "label": "End Date of Event",
+ "sortableAndFilterable": true,
+ "columnId": "EndDateOfEvent",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% this.event.contactInfo %>"
+ },
+ "label": "Contact Info",
+ "columnId": "ContactInfo",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "instanceList",
+ "instanceList": [
+ {
+ "id": "<% this.event.venue %>",
+ "descriptor": "<% this.event.venue.descriptor ?: '' %>"
+ }
+ ]
+ },
+ "label": "Booked Venue",
+ "columnId": "BookedVenue",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "readOnlyText",
+ "value": "<% this.event.internalOnly%>"
+ },
+ "label": "Internal Only?",
+ "columnId": "InternalOnly",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "instanceList",
+ "instanceList": [
+ {
+ "id": "<% this.event.createdBy %>",
+ "descriptor": "<% //here we use a ternary or Elvis expression to tell the page to check
+ // whether the descriptor is non-null. If it is null, display an empty string. Else, it will display the expression before the operator.
+ this.event.createdBy.descriptor ?: '' %>"
+ }
+ ]
+ },
+ "label": "Created By",
+ "columnId": "CreatedBy",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "section",
+ "children": [
+ {
+ "type": "button",
+ "action": "AUXILIARY",
+ "label": "Update",
+ "taskReference": {
+ "taskId": "updateEvent",
+ "parameterBindings": {
+ "workEventId": "<% /* this workEventId is a parameter we are passing to the update page once the user clicks to it.
+ taskId points to the target page, and on that page, the valid queryParam name in the inbond endpoint for the Event BO instance to update is populated here (event.id):*/
+ this.event.workdayID %>"
+ }
+ }
+ },
+ {
+ "type": "button",
+ "action": "AUXILIARY",
+ "label": "Update Image",
+ "taskReference": {
+ "taskId": "updateEventImage",
+ "parameterBindings": {
+ "workEventId": "<% this.event.workdayID %>"
+ }
+ }
+ }
+ ]
+ },
+ "columnId": "actions",
+ "type": "column",
+ "label": "Actions"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/microConfirm.pmd b/catalog/createAWorkEventWithAgent/presentation/microConfirm.pmd
new file mode 100644
index 0000000..35bb732
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/microConfirm.pmd
@@ -0,0 +1,34 @@
+{
+ "id": "microConfirm",
+ "endPoints": [ ],
+ "presentation": {
+ "headerSize": "VPS_DEFAULT",
+ "microConclusion": true,
+ "title": {
+ "type": "title",
+ "label": "Workday Extend"
+ },
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "text",
+ "id": "hello",
+ "label": "Hello World!",
+ "value": "Welcome from Workday!"
+ }
+ ]
+ },
+ "footer": {
+ "type": "footer",
+ "children": [
+ {
+ "type": "richText",
+ "enabled": "false",
+ "value": "Powered By Workday Extend"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/quickAddWorkEvent.pmd b/catalog/createAWorkEventWithAgent/presentation/quickAddWorkEvent.pmd
new file mode 100644
index 0000000..76431ad
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/quickAddWorkEvent.pmd
@@ -0,0 +1,235 @@
+{
+ "id": "quickAddWorkEvent",
+ "endPoints": [
+ {
+ "name": "me",
+ "baseUrlType": "workday-staffing",
+ "url": "workers/me",
+ "authType": "sso"
+ }
+ ],
+ "onLoad": "<%
+ var eventSelectedDates = queryParams.selectedDates.split(',');
+ pageVariables.eventStartDate = eventSelectedDates[0];
+ pageVariables.eventEndDate = eventSelectedDates[eventSelectedDates.size() - 1];
+ %>",
+ "presentation": {
+ "pageType": "edit",
+ "micro": true,
+ "title": {
+ "type": "title",
+ "id": "title",
+ "label": "<% 'Quick Add Work Event: ' + queryParams.createdByName %>"
+ },
+ "body": {
+ "type": "section",
+ "id": "bodySection",
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Name of Event",
+ "required": "<% true %>",
+ "valueOutBinding": "createEvent.name"
+ },
+ {
+ "type": "text",
+ "id": "location",
+ "label": "Proposed Event Location (free text)",
+ "required": "<% true %>",
+ "valueOutBinding": "createEvent.location"
+ },
+ {
+ "type": "richText",
+ "id": "description",
+ "label": "Description",
+ "required": "<% true %>",
+ "valueOutBinding": "createEvent.description",
+ "validations": [
+ {
+ "regex": "^.{0,1000}",
+ "errorMessage": "Maximum character count (including markup) reached: 1000"
+ }
+ ],
+ "aiPrompt": {
+ "buttons": [
+ {
+ "label": "Generate Work Event Description",
+ "prompt": "<% 'Create a description around 150 characters for' + name.value + ' and include event location ' + location.value %>",
+ "dependencyTags": [
+ "name",
+ "location"
+ ]
+ },
+ {
+ "label": "Refine My Event Description",
+ "prompt": "<% 'Summarize or expand following text to 100 words: ' + description.value %>",
+ "dependencyTags": [
+ "description"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "type": "text",
+ "id": "sponsor",
+ "label": "Event Sponsor",
+ "valueOutBinding": "createEvent.sponsor",
+ "guide": {
+ "type": "guide",
+ "id": "guideName",
+ "title": "Sponsor Name",
+ "text": " Enter the Event Sponsors name:"
+ }
+ },
+ {
+ "type": "date",
+ "id": "startdate",
+ "label": "Start Date of Event",
+ "value": "<% pageVariables.eventStartDate %>",
+ "valueOutBinding": "createEvent.startDate",
+ "required": true
+ },
+ {
+ "type": "date",
+ "id": "enddate",
+ "label": "End Date of Event",
+ "value": "<% pageVariables.eventEndDate %>",
+ "valueOutBinding": "createEvent.endDate",
+ "required": true
+ },
+ {
+ "type": "text",
+ "id": "contactInfo",
+ "label": "Contact Info/Email",
+ "valueOutBinding": "createEvent.contactInfo"
+ },
+ {
+ "type": "currency",
+ "currencyCode": "USD",
+ "id": "cost",
+ "label": "Fee, if any (USD)",
+ "minimumValue": "<% 0.01 %>",
+ "fractionDigits": 2,
+ "valuesOut": [
+ {
+ "value": "USD",
+ "valueOutBinding": "createEvent.cost.currency"
+ },
+ {
+ "value": "<% cost.value %>",
+ "valueOutBinding": "createEvent.cost.value"
+ }
+ ]
+ },
+ {
+ "type": "radioGroup",
+ "id": "internalOnlyRadioGroup",
+ "label": "Is this an internal only event?",
+ "hideDisplayOption": "true",
+ "required": "true",
+ "selectedValues": "<% ! empty internalOnlyRadioGroup.value ? (internalOnlyRadioGroup.value ? ['yes'] : ['no'] ) : ['no'] %>",
+ "instanceList": [
+ {
+ "id": "yes",
+ "descriptor": "Yes"
+ },
+ {
+ "id": "no",
+ "descriptor": "No"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "fileUploader",
+ "label": "Attach Files Related to the Event",
+ "id": "attachmentFileUploader",
+ "maxImageDimension": "1024",
+ "maxFileSize": "5242880",
+ "compact": "<% true %>",
+ "required": "<% false %>",
+ "fileTypes": "*",
+ "valueOutBinding": "<% 'eventAttachments:attachedFiles' //outbound endpoint name : attachment obect API URL
+ %> ",
+ "metadata": "<%
+ {:} //need at least an empty map in a metadata tag for onMultiPartSend() to have metadata to work with!
+ %>"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "createEvent",
+ "baseUrlType": "app",
+ "url": "workEvents",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "onSend": "<% //Outbound endpoint that submits the BO
+ //self.data is the payload as it exists coming into this onSend event
+ self.data.put('createdBy',{'id':me.id}); //add createdBy object
+ self.data.internalOnly = internalOnlyRadioGroup.value[0] == 'yes' ? true : false; //convert internalOnly radio group response from String to Boolean
+ self.data.createdBy.id = me.id; //add a createdBy value to our payload
+ console.info('new event payload is ' + json:asJSON(self.data)); return self.data; %>"
+ },
+ {
+ "name": "createWorkEventEvents",
+ "baseUrlType": "app",
+ "url": "createWorkEventEvents",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "onSend": "<%
+ //Kicks off BP. Pass in ID of the Work Event business object and when it is successfully created will trigger BP. Name element is the name of the BP in lower camel case + 'Events' from the .businessprocess file
+ console.info(json:asJSON(self.data));
+ return self.data;
+ %>",
+ "values": [
+ {
+ "outboundPath": "businessProcessTarget.id",
+ "value": "<% //value must match name of the outbound endpoint that submits the BO, here 'createEvent' and 'id' is the WID of that BO
+ createEvent.id %>"
+ }
+ ]
+ },
+ {
+ "name": "eventAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFilesCollection' //using the Collection endpoint to let us use onMultiPartSend() and to upload multiple files at once
+ %>",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "exclude": "<% empty(attachmentFileUploader.children) %>",
+ "onMultiPartSend": "<%
+ for (var file : self.data.metadata.attachedFiles) { //loop through as many files as were uploaded
+ //Put an uploadedBy object with an id field to the JSON part.
+ file.put('uploadedBy',{'id': me.id}); //put the new uploadedBy map in the metadata. This captures the WID of the uploader.
+ //Put uploadedDate field to the JSON part.
+ file.uploadedDate = date:getTodaysDate(date:getDateTimeZone(userTimeZone)); //put the uploadedDate field in the metadata
+ file.put('event',{'id': createEvent.id}); //link the file with the work event
+ console.info('metadata is ' + json:asJSON(self.data)); //log the metadata being passed in for debugging purposes
+ }
+ self.data;
+ %>"
+ }
+ ],
+ "responseErrorDetail": {
+ "errorSummary": "<% error %>",
+ "errors": "<% empty errors ? [] : [error].join(errors.map(err => { err.error } )) %>"
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/registerConfirmation.pmd b/catalog/createAWorkEventWithAgent/presentation/registerConfirmation.pmd
new file mode 100644
index 0000000..d9ca809
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/registerConfirmation.pmd
@@ -0,0 +1,43 @@
+{
+ "id": "registerConfirmation",
+ "securityDomains": [
+ "RegisterForEvents"
+ ],
+ "presentation": {
+ "headerSize": "HIDDEN",
+ "micro": "true",
+ "standardEditButtonsHidden": "<% true %>",
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "text",
+ "id": "successtext",
+ "value": "Your request to join the Work Event was successful!.",
+ "enabled": "false"
+ },
+ {
+ "action": "SECONDARY",
+ "id": "popupButton1",
+ "type": "button",
+ "taskReference": {
+ "type": "taskReference",
+ "taskId": "registerForEvents"
+ },
+ "label": "Registration Page"
+ },
+ {
+ "action": "SECONDARY",
+ "id": "popupButton2",
+ "type": "button",
+ "taskReference": {
+ "type": "taskReference",
+ "taskId": "calendarWorkEvents"
+ },
+ "label": "Calendar Page"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/registerForEvent.pmd b/catalog/createAWorkEventWithAgent/presentation/registerForEvent.pmd
new file mode 100644
index 0000000..2fa7c01
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/registerForEvent.pmd
@@ -0,0 +1,365 @@
+{
+ "id": "registerForEvent",
+ "securityDomains": [
+ "RegisterForEvents"
+ ],
+ "include": [
+ "fileTypes.script"
+ ],
+ "endPoints": [
+ {
+ "name": "me",
+ "baseUrlType": "workday-staffing",
+ "url": "workers/me",
+ "authType": "sso"
+ },
+ {
+ "name": "event",
+ "baseUrlType": "app",
+ "url": "<% /*remember that queryParams.workEventId is taking in the parameterBinding from the taskReference tag on the page
+ that sent the user here. This happens to be called 'workEventId' in manageEvents.pmd but it could be called whatever the developer chooses*/
+ 'workEvents/' + queryParams.workEventId %>",
+ "authType": "sso"
+ },
+ {
+ "name": "eventImage",
+ "baseUrlType": "app",
+ "url": "<% //won't fire the endpoint if
+ //there is no image saved to the
+ //event's 'image' field
+ 'eventImages/' + event.image.id %>",
+ "exclude": "<% empty(event.image) %>",
+ "authType": "sso"
+ },
+ {
+ "name": "alreadyRegistered",
+ "baseUrlType": "workday-wql",
+ "url": "<%
+ var query = 'SELECT worker FROM ' + site.applicationId + '_registrants where event = ' + queryParams.workEventId + ' AND worker = ' + me.id;
+ console.info('alreadyRegistered query is ' + json:asJSON(query));
+ return 'data?query=' + query.pathEncode(); %>",
+ "authType": "sso",
+ "failOnStatusCodes": [
+ {
+ "code": "400"
+ },
+ {
+ "code": "403"
+ }
+ ]
+ },
+ {
+ "name": "getAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFiles?event=' + queryParams.workEventId %>",
+ "authType": "sso"
+ }
+ ],
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "createRegistrant",
+ "baseUrlType": "app",
+ "url": "registrants",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "onSend": "<% //Outbound endpoint that submits the BO
+ //self.data is the payload as it exists coming into this onSend event from any valueOutBindings
+ let payload = {:}; //initialize new map to hold payload
+ payload.put('worker',{'id': me.id}); //add link to worker
+ payload.put('event',{'id': event.id}); //add link to work event
+ console.info('createRegistrant payload is ' + json:asJSON(payload)); return payload; %>"
+ },
+ {
+ "name": "updateEvent",
+ "baseUrlType": "app",
+ "url": "<% 'workEvents/' + event.id %>",
+ "httpMethod": "PATCH",
+ "authType": "sso",
+ "onSend": "<% console.info('updateEvent has been invoked for event ' + event.id);
+ console.info('createRegistrant response is ' + json:asJSON(createRegistrant));
+ let currentRegistrants = event.registrants ?? []; //get list of registrant objects, id + descriptor
+ let newRegistrant = {'id':createRegistrant.id}; //grab WID of BO just created and put it in object format
+ let allRegistrants = currentRegistrants.add(newRegistrant); //add BO instance to multi-instance list
+ console.info('new list is ' + json:asJSON(allRegistrants));
+ console.info('allRegistrants is ' + json:asJSON(allRegistrants));
+ let payload = {'registrants': allRegistrants}; //initializing payload to log and send
+ console.info('this updateEvent payload is ' + json:asJSON(payload));
+ return payload; //send payload
+ %>"
+ },
+ {
+ "name": "createEventAttachment",
+ "baseUrlType": "app",
+ "url": "attachedFilesCollection",
+ "authType": "sso",
+ "httpMethod": "POST",
+ "multiPartFileLast": true,
+ "onMultiPartSend": "<%
+ for (var file : self.data.metadata.attachedFiles) { //loop through as many files as were uploaded
+ //Put an uploadedBy object with an id field to the JSON part.
+ file.put('uploadedBy',{'id': me.id}); //put the new uploadedBy map in the metadata. This captures the WID of the uploader.
+ //Put uploadedDate field to the JSON part.
+ file.uploadedDate = date:getTodaysDate(date:getDateTimeZone(userTimeZone)); //put the uploadedDate field in the metadata
+ file.put('event',{'id': event.id}); //link the file with the charity
+ console.info('metadata is ' + json:asJSON(self.data)); //log the metadata being passed in for debugging purposes
+ }
+ self.data;
+ %>",
+ "exclude": "<% //This exclude statement checks whether a file was uploaded successfully!
+ empty attachmentFileUploader.children %>"
+ }
+ ]
+ },
+ "onLoad": "<%
+ pageVariables.debug = false;
+ pageVariables.hasError = false;
+ const alreadyApplied = !empty(alreadyRegistered.data); //if this has a result, worker already has a registration with this event
+ if (alreadyApplied == true) { //have I applied already
+ pageVariables.hasError = true;
+ }
+ %>",
+ "onSubmit": "<%
+ if (pageVariables.hasError == false) {
+ } else {
+ name.clearError(); //clear error and let submission go through
+ name.setError('You cannot request to register for an event if you already have a registration.'); //set error on first widget to prevent submission
+ }
+%>",
+ "presentation": {
+ "applicationExceptions": [
+ {
+ "type": "applicationException",
+ "severity": "WARNING",
+ "message": "<% 'You have already registered for this event.' %>",
+ "render": "<% pageVariables.hasError %>"
+ }
+ ],
+ "micro": "true",
+ "headerSize": "HIDDEN",
+ "title": {
+ "type": "title",
+ "label": "Update Event"
+ },
+ "body": {
+ "type": "section",
+ "children": [
+ {
+ "type": "editButtonBar",
+ "editButtons": [
+ {
+ "type": "editButton",
+ "label": "Register For Event",
+ "id": "RegisterButton",
+ "buttonType": "PRIMARY",
+ "ignoreRequiredFields": "true"
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "id": "leftside",
+ "children": [
+ {
+ "type": "fieldSet",
+ "id": "eventImage",
+ "title": "Event Image",
+ "render": "<% (!(empty(event.image))) %>",
+ "children": [
+ {
+ "type": "cardContainer",
+ "layout": "grid",
+ "cards": [
+ {
+ "type": "card",
+ "cardId": "EventImage",
+ "parameters": "<% {
+ 'title': '',
+ 'subtitle': '',
+ 'imageURL': 'https://api.workday.com/apps/'+site.applicationId+'/v1/eventImages/'+event.image.id
+ }%>"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Event Name",
+ "value": "<% event.name %>",
+ "enabled": false
+ },
+ {
+ "type": "richText",
+ "id": "description",
+ "label": "Description",
+ "value": "<% event.description %>",
+ "enabled": false
+ },
+ {
+ "type": "text",
+ "id": "location",
+ "label": "Proposed Event Location",
+ "value": "<% event.location %>",
+ "enabled": false
+ },
+ {
+ "type": "date",
+ "id": "startdate",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "Start Date of Event",
+ "value": "<% event.startDate %>",
+ "enabled": false
+ },
+ {
+ "type": "date",
+ "id": "enddate",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "End Date of Event",
+ "value": "<% event.endDate %>",
+ "enabled": false
+ },
+ {
+ "type": "text",
+ "id": "sponsor",
+ "label": "Event Sponsor",
+ "value": "<% event.sponsor %>",
+ "enabled": false
+ },
+ {
+ "type": "currency",
+ "currencyCode": "USD",
+ "id": "cost",
+ "fractionDigits": 2,
+ "label": "Fee (USD)",
+ "value": "<% //the value to display on page load, in this case the existing value from the inbound endpoint
+ event.cost.value ?? 0 %>",
+ "enabled": false
+ },
+ {
+ "type": "text",
+ "id": "contactInfo",
+ "label": "Contact Info/Email",
+ "value": "<% event.contactInfo %>",
+ "enabled": false
+ },
+ {
+ "type": "instanceList",
+ "id": "registrants",
+ "values": "<% event.registrants %>",
+ "label": "Current Registrants (prompt to open list)"
+ },
+ {
+ "type": "radioGroup",
+ "id": "internalOnlyRadioGroup",
+ "label": "Is this an internal only event?",
+ "hideDisplayOption": "true",
+ "required": "true",
+ "enabled": false,
+ "selectedValues": "<% ! empty event.internalOnly ? (event.internalOnly ? ['yes'] : ['no'] ) : ['no'] %>",
+ "instanceList": [
+ {
+ "id": "yes",
+ "descriptor": "Yes"
+ },
+ {
+ "id": "no",
+ "descriptor": "No"
+ }
+ ]
+ },
+ {
+ "type": "fieldSet",
+ "id": "updateEventFieldSet",
+ "title": "Click 'Update' to Edit the Event",
+ "children": [
+ {
+ "type": "button",
+ "action": "AUXILIARY",
+ "label": "Update",
+ "taskReference": {
+ "taskId": "updateEvent",
+ "parameterBindings": {
+ "workEventId": "<% /* this workEventId is a parameter we are passing to the update page once the user clicks to it.
+ taskId points to the target page, and on that page, the valid queryParam name in the inbond endpoint for the Event BO instance to update is populated here (event.id):*/
+ event.id %>"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "id": "rightside",
+ "render": "<% !empty(getAttachments.data) %>",
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "fileUploader",
+ "label": "Attach Files Related to the Event",
+ "id": "attachmentFileUploader",
+ "maxImageDimension": "1024",
+ "maxFileSize": "5242880",
+ "compact": "<% true %>",
+ "required": "<% false %>",
+ "fileTypes": "*",
+ "valueOutBinding": "<% 'createEventAttachment:attachedFiles' //outbound endpoint name : attachment obect API URL
+ %> ",
+ "metadata": "<%
+ {:} //need at least an empty map in a metadata tag for onMultiPartSend() to have metadata to work with!
+ %>"
+ }
+ ]
+ },
+ {
+ "type": "fileUploader",
+ "label": "Previously attached files for Event:",
+ "id": "multiFileUploader",
+ "valueOutBinding": "postAttachments:filedata:",
+ "enabled": "false",
+ "fileTypes": "txt,jpg,jpeg,png,ppt,pptx,pdf,doc,docx,xlsx,xls",
+ "singular": false,
+ "children": [
+ {
+ "type": "loop",
+ "on": "<% getAttachments.data %>",
+ "as": "file",
+ "children": [
+ {
+ "type": "fileUploaderRow",
+ "render": "<% !(empty file.fileName) %>",
+ "attachment": {
+ "type": "attachmentList",
+ "attachmentURL": "https://api.workday.com/apps/<%site.applicationId%>/v1/attachedFiles/<%file.id%>?event=<%queryParams.workEventId%>",
+ "attachmentAuth": "sso",
+ "attachmentName": "<% file.fileName %>",
+ "attachmentUriTemplate": "[Target]",
+ "instanceList": [
+ {
+ "action": "attachment",
+ "descriptor": "<%file.fileName%>",
+ "mimeType": "<% fileType:getFileType(file.fileName) %>"
+ }
+ ]
+ },
+ "fileName": "<%file.fileName%>",
+ "fileType": "<%fileType:getFileType(file.fileName) %>"
+ }
+ ]
+ }
+ ],
+ "fileUploaderRowTemplate": [ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/registerForEvents.pmd b/catalog/createAWorkEventWithAgent/presentation/registerForEvents.pmd
new file mode 100644
index 0000000..369cace
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/registerForEvents.pmd
@@ -0,0 +1,268 @@
+{
+ "id": "registerForEvents",
+ "securityDomains": [
+ "RegisterForEvents"
+ ],
+ "endPoints": [
+ {
+ "name": "me",
+ "baseUrlType": "graph",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "failOnStatusCodes": [
+ {
+ "code": "400"
+ },
+ {
+ "code": "403"
+ }
+ ],
+ "graphQuery": {
+ "queryId": "me"
+ }
+ },
+ {
+ "name": "events",
+ "baseUrlType": "workday-wql",
+ "url": "<% const query = 'SELECT name, cost, description, workdayID as id, sponsor, location, startDate, endDate, contactInfo, venue, createdBy, registrants { worker } FROM ' + site.applicationId + '_workEvents';
+ console.info('events query is ' + query);
+ return 'data?query=' + query.urlEncode();
+ %>",
+ "authType": "sso",
+ "failOnStatusCodes": [
+ {
+ "code": "400"
+ },
+ {
+ "code": "403"
+ }
+ ]
+ }
+ ],
+ "presentation": {
+ "title": {
+ "type": "title",
+ "label": "Register for an Event"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "section",
+ "horizontal": true,
+ "children": [
+ {
+ "type": "richText",
+ "value": "Use this page to browse for upcoming Work Events to register for."
+ },
+ {
+ "type": "cardContainer",
+ "layout": "MASONRY",
+ "cards": [
+ {
+ "type": "card",
+ "cardId": "CalendarLinks"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "eventsGrid",
+ "label": "Upcoming Work Events",
+ "type": "grid",
+ "rows": "<% //only return events that haven't yet taken place. date:parse() ensures the date strings for today's date and the event dates can be compared using PMD Scripting
+ events.data.filter(event => {date:parse(event.startDate).after(date:now(userTimeZone)) }) %>",
+ "rowVariableName": "event",
+ "columns": [
+ {
+ "cellTemplate": {
+ "type": "button",
+ "label": "<% event.name %>",
+ "action": "LINK",
+ "value": "<% event.id %>",
+ "taskReference": {
+ "taskId": "updateEvent",
+ "parameterBindings": {
+ "workEventId": "<% /* this workEventId is a parameter we are passing to the update page once the user clicks to it.
+ taskId points to the target page, and on that page, the valid queryParam name in the inbond endpoint for the Event BO instance to update is populated here (event.id):*/
+ event.id %>"
+ }
+ }
+ },
+ "label": "Name of Event",
+ "type": "column",
+ "columnId": "name",
+ "sortableAndFilterable": true
+ },
+ {
+ "cellTemplate": {
+ "type": "currency",
+ "label": "Amount (USD)",
+ "id": "cost",
+ "enabled": false,
+ "currencyCode": "USD",
+ "value": "<% event.cost.value ?: 0 %>",
+ "showCurrencySymbol": true,
+ "render": true,
+ "visible": true
+ },
+ "label": "Event Fee",
+ "columnId": "Cost",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "richText",
+ "value": "<% event.description %>"
+ },
+ "label": "Description",
+ "columnId": "Description",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% event.sponsor %>"
+ },
+ "label": "Event Sponsor",
+ "sortableAndFilterable": true,
+ "columnId": "Sponsor",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% event.location %>"
+ },
+ "label": "Location",
+ "columnId": "Proposed Location",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "date",
+ "datePrecision": "DAY",
+ "dateFormat": "yyyy-MM-dd",
+ "value": "<% event.startDate %>"
+ },
+ "label": "Start Date of Event",
+ "sortableAndFilterable": true,
+ "columnId": "StartDateOfEvent",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "date",
+ "datePrecision": "DAY",
+ "dateFormat": "yyyy-MM-dd",
+ "value": "<% event.endDate %>"
+ },
+ "label": "End Date of Event",
+ "sortableAndFilterable": true,
+ "columnId": "EndDateOfEvent",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% event.contactInfo %>"
+ },
+ "label": "Contact Info",
+ "columnId": "ContactInfo",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "text",
+ "value": "<% let registrantsList = event.registrants ?? [];
+ let it_me = registrantsList.find(x => x.worker.id == me.data.worker.data[0].workdayID.id);
+ if (it_me != null) { 'true'; } %>"
+ },
+ "label": "Currently Registered?",
+ "columnId": "registrants",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "instanceList",
+ "instanceList": [
+ {
+ "id": "<% event.venue %>",
+ "descriptor": "<% event.venue.descriptor ?: '' %>"
+ }
+ ]
+ },
+ "label": "Booked Venue",
+ "columnId": "BookedVenue",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "readOnlyText",
+ "value": "<% event.internalOnly%>"
+ },
+ "label": "Internal Only?",
+ "columnId": "InternalOnly",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "instanceList",
+ "instanceList": [
+ {
+ "id": "<% event.createdBy %>",
+ "descriptor": "<% //here we use a ternary or Elvis expression to tell the page to check
+ // whether the descriptor is non-null. If it is null, display an empty string. Else, it will display the expression before the operator.
+ event.createdBy.descriptor ?: '' %>"
+ }
+ ]
+ },
+ "label": "Created By",
+ "columnId": "CreatedBy",
+ "type": "column"
+ },
+ {
+ "cellTemplate": {
+ "type": "section",
+ "children": [
+ {
+ "type": "button",
+ "action": "AUXILIARY",
+ "label": "Register",
+ "visible": "<%
+ let registrantsList = event.registrants ?? [];
+ let it_me = registrantsList.find(x => x.worker.id == me.data.worker.data[0].workdayID.id);
+ empty(it_me);
+ %>",
+ "taskReference": {
+ "taskId": "registerForEvent",
+ "parameterBindings": {
+ "workEventId": "<% /* this workEventId is a parameter we are passing to the update page once the user clicks to it.
+ taskId points to the target page, and on that page, the valid queryParam name in the inbond endpoint for the Event BO instance to update is populated here (event.id):*/
+ event.id %>"
+ }
+ }
+ },
+ {
+ "type": "text",
+ "value": "Successfully Registered",
+ "visible": "<% let registrantsList = event.registrants ?? [];
+ let it_me = registrantsList.find(x => x.worker.id == me.data.worker.data[0].workdayID.id);
+ !empty(it_me); %>"
+ }
+ ]
+ },
+ "columnId": "actions",
+ "type": "column",
+ "label": "Actions"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/scripts/fileTypes.script b/catalog/createAWorkEventWithAgent/presentation/scripts/fileTypes.script
new file mode 100644
index 0000000..a0bb5f9
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/scripts/fileTypes.script
@@ -0,0 +1,185 @@
+var getData = function(){
+ var data = [
+{
+ "mimeType": "text/plain",
+ "iconId": "mime_plain",
+ "contentType": ""
+ },
+{
+ "mimeType": "application/java-archive",
+ "iconId": "mime_java",
+ "contentType": ""
+ },
+{
+ "mimeType": "application/octet-stream",
+ "iconId": "mime_binary",
+ "contentType": ""
+ },
+{
+ "mimeType": "application/vnd.ms-excel",
+ "iconId": "mime_msexcel",
+ "contentType": "398c2a9708d0100003f96143c9430000"
+ },
+{
+ "mimeType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "iconId": "mime_msexcel",
+ "contentType": "398c2a9708d0100003f96143c9430000"
+ },
+{
+ "mimeType": "application/vnd.ms-powerpoint",
+ "iconId": "mime_msppt",
+ "contentType": "bda0e171ee8610001fdfe43554e0008b"
+ },
+{
+ "mimeType": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ "iconId": "mime_msppt",
+ "contentType": "bda0e171ee8610001fdfe43554e0008b"
+ },
+{
+ "mimeType": "application/msword",
+ "iconId": "mime_msword",
+ "contentType": "73bc2567331510005b2cfa77095506f8"
+ },
+{
+ "mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "iconId": "mime_msword",
+ "contentType": "73bc2567331510005b2cfa77095506f8"
+ },
+{
+ "mimeType": "application/pdf",
+ "iconId": "mime_pdf",
+ "contentType": "d9ae3cc8446c11de98360015c5e6daf6"
+ },
+{
+ "mimeType": "mime_post",
+ "iconId": "mime_post",
+ "_comment": "No idea what file type this maps to.",
+ "contentType": ""
+ },
+{
+ "mimeType": "application/rtf",
+ "iconId": "mime_rtf",
+ "contentType": ""
+ },
+{
+ "mimeType": "application/zip",
+ "iconId": "mime_zip",
+ "contentType": ""
+ },
+{
+ "mimeType": "audio/wav",
+ "iconId": "mime_wav",
+ "contentType": "d9ae5154446c11de98360015c5e6daf6"
+ },
+{
+ "mimeType": "image/bmp",
+ "iconId": "mime_image",
+ "contentType": ""
+ },
+{
+ "mimeType": "image/gif",
+ "iconId": "mime_image",
+ "contentType": "d9ae5ed8446c11de98360015c5e6daf6"
+ },
+{
+ "mimeType": "image/jpeg",
+ "iconId": "mime_image",
+ "contentType": "d9ae5d84446c11de98360015c5e6daf6"
+ },
+{
+ "mimeType": "image/png",
+ "iconId": "mime_image",
+ "contentType": "41291951c48210003267802b40f3a841"
+ },
+{
+ "mimeType": "image/svg+xml",
+ "iconId": "mime_image",
+ "contentType": ""
+ },
+{
+ "mimeType": "image/tiff",
+ "iconId": "mime_image",
+ "_comment": "There are likely more image mimetypes that are supported....but this is a good start.",
+ "contentType": ""
+ },
+{
+ "mimeType": "video/mpeg",
+ "iconId": "mime_mpeg",
+ "contentType": ""
+ },
+{
+ "mimeType": "text/html",
+ "iconId": "mime_html",
+ "contentType": "d9ae66b2446c11de98360015c5e6daf6"
+ },
+{
+ "mimeType": "mime_etf",
+ "iconId": "mime_etf",
+ "_comment": "No idea what file type this maps to.",
+ "contentType": ""
+ },
+{
+ "mimeType": "application/xml",
+ "iconId": "mime_xml",
+ "contentType": "d9ae655e446c11de98360015c5e6daf6"
+ },
+{
+ "mimeType": "text/xml",
+ "iconId": "mime_xml",
+ "contentType": "d9ae655e446c11de98360015c5e6daf6"
+ },
+{
+ "mimeType": "video/x-msvideo",
+ "iconId": "mime_avi",
+ "contentType": ""
+ },
+{
+ "mimeType": "mime_qt",
+ "iconId": "mime_qt",
+ "_comment": "No idea what file type this maps to.",
+ "contentType": ""
+ },
+{
+ "mimeType": "mime_msvideo",
+ "iconId": "mime_msvideo",
+ "_comment": "Both this one AND the mime_avi appear to map to the same mimeType.",
+ "contentType": ""
+ }
+ ]
+ ;
+
+}
+
+
+var getIconID = function(myInput) {
+
+ var input = myInput;
+ var result = getData().filter(x => {x.mimeType == input});
+
+ var icon = result[0].iconId ?: '';
+ return icon;
+};
+
+var getIconIDbyContentType = function(myInput) {
+
+ var input = myInput;
+ var result = getData().filter(x => {x.contentType == input});
+
+ var icon = result[0].iconId ?: '';
+ return icon;
+};
+
+var getMimeTypeIDbyContentType = function(myInput) {
+
+ var input = myInput;
+ var result = getData().filter(x => {x.contentType == input});
+
+ var icon = result[0].mimeType ?: '';
+ return icon;
+};
+
+{
+ "getIconID": getIconID,
+ "getIconIDbyContentType": getIconIDbyContentType,
+ "getMimeTypeIDbyContentType": getMimeTypeIDbyContentType
+}
diff --git a/catalog/createAWorkEventWithAgent/presentation/sidebarInfo.pmd b/catalog/createAWorkEventWithAgent/presentation/sidebarInfo.pmd
new file mode 100644
index 0000000..63d399b
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/sidebarInfo.pmd
@@ -0,0 +1,56 @@
+{
+ "id": "sidebarInfo",
+ "presentation": {
+ "body": {
+ "type": "section",
+ "children": [
+ {
+ "type": "readOnlyText",
+ "label": "Welcome!",
+ "value": "Use this calendar to plan for and view upcoming Work Events.",
+ "enabled": false
+ },
+ {
+ "type": "readOnlyText",
+ "label": "How to Use:",
+ "value": "Clicking dates on the calendar and hitting 'Create Work Event' will create a new event and will prompt you for event details, and the event can then be submitted for manager approval. Note that an HR Administrator will help select the final approved location.",
+ "enabled": false
+ },
+ {
+ "type": "readOnlyText",
+ "value": "Click into an existing event to register for an event. You can only register for an event once.",
+ "enabled": false
+ },
+ {
+ "type": "text",
+ "label": "Your Title",
+ "value": "<% queryParams.businessTitle %>",
+ "enabled": false
+ },
+ {
+ "type": "text",
+ "label": "Your Primary Office Location",
+ "value": "<% queryParams.primaryLocation %>",
+ "enabled": false
+ },
+ {
+ "type": "readOnlyText",
+ "value": "
+
+ ",
+ "enabled": false
+ },
+ {
+ "type": "button",
+ "id": "home",
+ "label": "Return to the Home Page",
+ "taskReference": {
+ "type": "taskReference",
+ "taskId": "eventHub"
+ },
+ "action": "AUXILIARY"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/updateEvent.pmd b/catalog/createAWorkEventWithAgent/presentation/updateEvent.pmd
new file mode 100644
index 0000000..c5be612
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/updateEvent.pmd
@@ -0,0 +1,287 @@
+{
+ "id": "updateEvent",
+ "securityDomains": [
+ "ManageCreateAnEvent"
+ ],
+ "include": [
+ "fileTypes.script"
+ ],
+ "endPoints": [
+ {
+ "name": "me",
+ "baseUrlType": "workday-staffing",
+ "url": "workers/me",
+ "authType": "sso"
+ },
+ {
+ "name": "event",
+ "baseUrlType": "app",
+ "url": "<% /*remember that queryParams.eventId is taking in the parameterBinding from the taskReference tag on the page
+ that sent the user here. This happens to be called 'workEventId' in manageEvents.pmd but it could be called whatever the developer chooses*/
+ 'workEvents/' + (queryParams.workEventId ?? flowVariables.currentEvent) %>",
+ "authType": "sso"
+ },
+ {
+ "name": "getAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFiles?event=' + (flowVariables.currentEvent ?? queryParams.workEventId) %>",
+ "authType": "sso"
+ }
+ ],
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "editButtonBarFlowVariable",
+ "type": "outboundVariable",
+ "variableScope": "flow",
+ "values": [
+ {
+ "outboundPath": "deleteClicked",
+ "value": "<% deleteButton.value %>"
+ },
+ {
+ "outboundPath": "currentEvent",
+ "value": "<% (flowVariables.currentEvent ?? queryParams.workEventId) %>"
+ }
+ ]
+ },
+ {
+ "name": "updateEvent",
+ "baseUrlType": "app",
+ "url": "<% 'workEvents/' + event.id %>",
+ "authType": "sso",
+ "httpMethod": "PUT",
+ "allowPutForUnchangedData": true,
+ "onSend": "<% //the above allowPutForUnchangedData attribute will cause the PUT to fire upon page submission even if no data appears to have changed
+ //Outbound endpoint that updates the BO. We need to provide all existing values on the Event so they do not fall off since this is a PUT
+ //it is always preferable to add to your payload using onSend as opposed to a values[] array within the outboundEndpoint. Use the following syntax.
+ //self.data is the payload as it exists coming into this onSend event
+ self.data.internalOnly = internalOnlyRadioGroup.value[0] == 'yes' ? true : false; //convert internalOnly radio group response from String to Boolean
+ self.data.description = description.value;
+ self.data.put('createdBy',{'id': event.createdBy.id}); //add createdBy object
+ console.info(json:asJSON(self.data)); return self.data; %>"
+ },
+ {
+ "name": "createEventAttachment",
+ "baseUrlType": "app",
+ "url": "attachedFilesCollection",
+ "authType": "sso",
+ "httpMethod": "POST",
+ "multiPartFileLast": true,
+ "onMultiPartSend": "<%
+ for (var file : self.data.metadata.attachedFiles) { //loop through as many files as were uploaded
+ //Put an uploadedBy object with an id field to the JSON part.
+ file.put('uploadedBy',{'id': me.id}); //put the new uploadedBy map in the metadata. This captures the WID of the uploader.
+ //Put uploadedDate field to the JSON part.
+ file.uploadedDate = date:getTodaysDate(date:getDateTimeZone(userTimeZone)); //put the uploadedDate field in the metadata
+ file.put('event',{'id': event.id}); //link the file with the charity
+ console.info('metadata is ' + json:asJSON(self.data)); //log the metadata being passed in for debugging purposes
+ }
+ self.data;
+ %>",
+ "exclude": "<% //This exclude statement checks whether a file was uploaded successfully!
+ empty attachmentFileUploader.children %>"
+ }
+ ],
+ "responseErrorDetail": {
+ "errorSummary": "<% error %>",
+ "errors": "<% errors.map(item => { item.error }); %>"
+ }
+ },
+ "presentation": {
+ "title": {
+ "type": "title",
+ "label": "Update Event"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": true,
+ "children": [
+ {
+ "type": "section",
+ "id": "leftside",
+ "children": [
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Event Name",
+ "required": "<% true %>",
+ "value": "<% event.name %>",
+ "valueOutBinding": "updateEvent.name"
+ },
+ {
+ "type": "richText",
+ "id": "description",
+ "label": "Description",
+ "required": "<% true %>",
+ "value": "<% event.description %>"
+ },
+ {
+ "type": "text",
+ "id": "location",
+ "label": "Event Location",
+ "required": "<% true %>",
+ "value": "<% event.location %>",
+ "valueOutBinding": "updateEvent.location"
+ },
+ {
+ "type": "date",
+ "id": "startdate",
+ "required": "<% true %>",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "Start Date of Event",
+ "value": "<% event.startDate %>",
+ "valueOutBinding": "updateEvent.startDate"
+ },
+ {
+ "type": "date",
+ "id": "enddate",
+ "required": "<% true %>",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "End Date of Event",
+ "value": "<% event.endDate %>",
+ "valueOutBinding": "updateEvent.endDate"
+ },
+ {
+ "type": "text",
+ "id": "sponsor",
+ "label": "Event Sponsor",
+ "value": "<% event.sponsor %>",
+ "valueOutBinding": "updateEvent.sponsor"
+ },
+ {
+ "type": "currency",
+ "currencyCode": "USD",
+ "id": "cost",
+ "fractionDigits": 2,
+ "label": "Fee (USD)",
+ "value": "<% //the value to display on page load, in this case the existing value from the inbound endpoint
+ event.cost.value ?? 0 %>",
+ "valuesOut": [
+ {
+ "value": "USD",
+ "valueOutBinding": "updateEvent.cost.currency"
+ },
+ {
+ "value": "<% //the value the user enters, whereas the valueOutBinding below is what will hit the outbound endpoint
+ self.value %>",
+ "valueOutBinding": "updateEvent.cost.value"
+ }
+ ]
+ },
+ {
+ "type": "text",
+ "id": "contactInfo",
+ "label": "Contact Info/Email",
+ "value": "<% event.contactInfo %>",
+ "valueOutBinding": "updateEvent.contactInfo"
+ },
+ {
+ "type": "radioGroup",
+ "id": "internalOnlyRadioGroup",
+ "label": "Is this an internal only event?",
+ "hideDisplayOption": "true",
+ "required": "true",
+ "selectedValues": "<% ! empty event.internalOnly ? (event.internalOnly ? ['yes'] : ['no'] ) : ['no'] %>",
+ "instanceList": [
+ {
+ "id": "yes",
+ "descriptor": "Yes"
+ },
+ {
+ "id": "no",
+ "descriptor": "No"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "id": "rightside",
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "fileUploader",
+ "label": "Attach Files Related to the Event",
+ "id": "attachmentFileUploader",
+ "maxImageDimension": "1024",
+ "maxFileSize": "5242880",
+ "compact": "<% true %>",
+ "required": "<% false %>",
+ "fileTypes": "*",
+ "valueOutBinding": "<% 'createEventAttachment:attachedFiles' //outbound endpoint name : attachment obect API URL
+ %> ",
+ "metadata": "<%
+ {:} //need at least an empty map in a metadata tag for onMultiPartSend() to have metadata to work with!
+ %>"
+ }
+ ]
+ },
+ {
+ "type": "fileUploader",
+ "label": "Previously attached files for Event:",
+ "id": "multiFileUploader",
+ "valueOutBinding": "postAttachments:filedata:",
+ "enabled": "false",
+ "fileTypes": "txt,jpg,jpeg,png,ppt,pptx,pdf,doc,docx,xlsx,xls",
+ "singular": false,
+ "children": [
+ {
+ "type": "loop",
+ "on": "<% getAttachments.data %>",
+ "as": "file",
+ "children": [
+ {
+ "type": "fileUploaderRow",
+ "render": "<% !(empty file.fileName) %>",
+ "attachment": {
+ "type": "attachmentList",
+ "attachmentURL": "<% 'https://api.workday.com/apps/' + site.applicationId + '/v1/attachedFiles/' + file.id %>",
+ "attachmentAuth": "sso",
+ "attachmentName": "<% file.fileName %>",
+ "attachmentUriTemplate": "[Target]",
+ "instanceList": [
+ {
+ "action": "attachment",
+ "descriptor": "<% file.fileName%>",
+ "mimeType": "<% fileType:getFileType(file.fileName) %>"
+ }
+ ]
+ },
+ "fileName": "<% file.fileName%>",
+ "fileType": "<% fileTypes.getMimeTypeIDbyContentType(file.contentType.id) %>"
+ }
+ ]
+ }
+ ],
+ "fileUploaderRowTemplate": [ ]
+ }
+ ]
+ },
+ {
+ "type": "editButtonBar",
+ "editButtons": [
+ {
+ "type": "editButton",
+ "label": "Update Event",
+ "id": "updateEventButton",
+ "buttonType": "PRIMARY"
+ },
+ {
+ "type": "editButton",
+ "label": "Remove Attachments",
+ "id": "deleteButton",
+ "buttonType": "AUXILIARY"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/updateEventImage.pmd b/catalog/createAWorkEventWithAgent/presentation/updateEventImage.pmd
new file mode 100644
index 0000000..00956c7
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/updateEventImage.pmd
@@ -0,0 +1,242 @@
+{
+ "id": "updateEventImage",
+ "securityDomains": [
+ "ManageCreateAnEvent"
+ ],
+ "endPoints": [
+ {
+ "name": "worker",
+ "baseUrlType": "workday-staffing",
+ "url": "workers/me",
+ "authType": "sso"
+ },
+ {
+ "name": "event",
+ "baseUrlType": "app",
+ "url": "<% 'workEvents/' + queryParams.workEventId %>",
+ "authType": "sso"
+ },
+ {
+ "name": "eventImage",
+ "baseUrlType": "app",
+ "url": "<% //won't fire the endpoint if
+ //there is no image saved to the
+ //event's 'image' field
+ 'eventImages/' + event.image.id %>",
+ "exclude": "<% empty(event.image) %>",
+ "authType": "sso"
+ },
+ {
+ "name": "eventImageMetadata",
+ "baseUrlType": "app",
+ "url": "<% //Get the Event Image metadata by specifying the Accept header as JSON. Model Attachments are files that can be stored
+ //alongside JSON metadata in a multipart request (JSON metadata in one part, the file contents in another). This request hits the exact same endpoint
+ //as 'eventImage' above, with the only difference being that it will specifically consume the JSON metadata associated with the file contents in eventImage
+ 'eventImages/' + event.image.id %>",
+ "exclude": "<% empty(event.image)%>",
+ "authType": "sso",
+ "headers": [
+ {
+ "name": "Accept",
+ "value": "application/json"
+ }
+ ]
+ }
+ ],
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "createEventImage",
+ "baseUrlType": "app",
+ "url": "eventImages",
+ "authType": "sso",
+ "httpMethod": "POST",
+ "multiPartFileLast": true,
+ "onSend": "<%
+ //logs the createEventImage payload to the console and then submits the request
+ //console.info('createEventImage' + json:asJSON(self.data));
+ return self.data;
+ %>",
+ "exclude": "<% //This exclude statement checks whether a file was uploaded successfully!
+ empty imageFileUploader.children %>"
+ },
+ {
+ "name": "updateEvent",
+ "baseUrlType": "app",
+ "url": "<% //Set the newly created Event Image on the Event since the relationship did not previously exist.
+ 'workEvents/' + event.id %>",
+ "authType": "sso",
+ "onSend": "<%
+ //logs the updateEvent payload to the console and then submits the request
+ console.info(json:asJSON(self.data));
+ return self.data;
+ %>",
+ "exclude": "<% //This exclude statement checks whether the previous outbound endpoint returned an ID, which will only be true if it fired successfully
+ empty createEventImage.id %>",
+ "httpMethod": "PATCH",
+ "values": [
+ {
+ "outboundPath": "image.id",
+ "value": "<% //Set Event Image relationship with ID returned by createEventImage endpoint.
+ createEventImage.id %>"
+ }
+ ]
+ },
+ {
+ "name": "deleteOldImage",
+ "baseUrlType": "app",
+ "url": "<% 'eventImages/' + event.image.id %>",
+ "authType": "sso",
+ "httpMethod": "DELETE",
+ "multiPartFileLast": true,
+ "onSend": "<%
+ //console.info(json:asJSON(self.data));
+ return self.data;
+ %>",
+ "exclude": "<% //Delete prior Event Image if there is one already there
+ empty(event.image) %>"
+ }
+ ],
+ "responseErrorDetail": {
+ "errorSummary": "<% error %>",
+ "errors": "<% errors.map(item => { item.error }); %>"
+ }
+ },
+ "presentation": {
+ "title": {
+ "type": "title",
+ "label": "Update Event Image"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "title": "Upload an Event Image",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Name of Event",
+ "value": "<% event.name %>",
+ "enabled": "<% false %>"
+ },
+ {
+ "type": "section",
+ "horizontal": true,
+ "sorted": false,
+ "id": "bothLeftAndRightSides",
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "id": "leftside",
+ "children": [
+ {
+ "type": "hidden",
+ "value": "<% worker.id %>",
+ "valueOutBinding": "<% //note the use of PMD multipart/form-data syntax outboundEndpointName:partName:field
+ //where the field 'uploadedBy.id' will be populated in a 'data' part for the request
+ //these fields make up the exact kind of 'metadata' we retrieved above with the endpoint using theh 'Accept: application/json' header
+ 'createEventImage:data:uploadedBy.id' %>"
+ },
+ {
+ "type": "hidden",
+ "value": "<% date:getTodaysDate(null) %>",
+ "valueOutBinding": "<% //note the use of PMD multipart/form-data syntax outboundEndpointName:partName:field
+ //where the field 'uploadedDate' will be populated in a 'data' part for the request
+ //these fields make up the exact kind of 'metadata' we retrieved above with the endpoint using theh 'Accept: application/json' header
+ 'createEventImage:data:uploadedDate' %>"
+ },
+ {
+ "type": "hidden",
+ "value": "<% //the content-id or 'cid' associated with the file binary that will be sent by fileUploader
+ //the cid: prefix tells the app which 'partName' in the multipart request the actual file contents will go to. The name 'image' here is arbitrary
+ //all that matters is that 'image' here matches the partName in the fileUploader's valueOutBinding below, e.g. 'image' here matches the 'image'
+ //in 'createEventImage:image'
+ 'cid:image' %>",
+ "valueOutBinding": "<% //note the use of PMD multipart/form-data syntax outboundEndpointName:partName:field
+ //where the field 'file' will be populated in a 'data' part for the request
+ //these fields make up the exact kind of 'metadata' we retrieved above with the endpoint using the 'Accept: application/json' header
+ //this elemenet of the multipart request MUST be outboundEndpointName:data:file where outboundEndpointName is the name of the outbound endpoint
+ //receiving the file from fileUploader
+ 'createEventImage:data:file' %>"
+ },
+ {
+ "type": "fileUploader",
+ "id": "imageFileUploader",
+ "label": "New Image",
+ "singular": "<% true %>",
+ "maxImageDimension": "1024",
+ "maxFileSize": "5242880",
+ "compact": "<% false %>",
+ "required": "<% true %>",
+ "fileTypes": "gif,jpg,jpeg,png",
+ "valueOutBinding": "<% //notice that when using fileUploader for multipart data (necessary for Model Attachment requests) this value will be
+ //outboundEndpointName + ':' + partName after 'cid:' above
+ 'createEventImage:image'%>"
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "title": "Current Image",
+ "id": "rightside",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "section",
+ "id": "currentImageInfo",
+ "horizontal": false,
+ "sorted": false,
+ "render": "<% (!(empty(event.image))) %>",
+ "children": [
+ {
+ "type": "image",
+ "sizingHint": "INLINE",
+ "id": "currentEventImage",
+ "endPoint": {
+ "url": "eventImages/<% event.image.id %>",
+ "baseUrlType": "app",
+ "authType": "sso"
+ }
+ },
+ {
+ "type": "text",
+ "id": "uploadedBy",
+ "label": "Uploaded By",
+ "value": "<% eventImageMetadata.uploadedBy.descriptor %>",
+ "enabled": "<% false %>"
+ },
+ {
+ "type": "text",
+ "label": "Uploaded Date",
+ "id": "uploadedDate",
+ "enabled": "<% false %>",
+ "value": "<% eventImageMetadata.uploadedDate %>"
+ },
+ {
+ "type": "text",
+ "label": "File Size",
+ "id": "fileSize",
+ "value": "<% file:byteCountToDisplaySize(eventImageMetadata.fileLength) %>",
+ "enabled": "<% false %>"
+ }
+ ]
+ },
+ {
+ "type": "readOnlyText",
+ "id": "noImageMessage",
+ "value": "There is currently no image for this Event.",
+ "render": "<% empty(event.image) %>"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/viewRequestByEventID.pmd b/catalog/createAWorkEventWithAgent/presentation/viewRequestByEventID.pmd
new file mode 100644
index 0000000..07577da
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/viewRequestByEventID.pmd
@@ -0,0 +1,199 @@
+{
+ "id": "viewRequestByEventID",
+ "include": [
+ "fileTypes.script"
+ ],
+ "endPoints": [
+ {
+ "baseUrlType": "workday-wql",
+ "name": "getRequestId",
+ "authType": "sso",
+ "url": "<% //Grab the request ID using the BP Event ID. queryParams.eventId will automatically pull in the BP Event ID if it is in the page's scope
+ var query = 'SELECT businessProcessTarget FROM ' + site.applicationId + '_createWorkEventEvents WHERE workdayID = \"' + queryParams.eventId + '\"';
+ //if you reuse this code be sure to replace _createWorkEventEvents above with the appropriate endpoint for your Model BP
+ console.info(query);
+ return 'data?query=' + query.pathEncode() %>",
+ "failOnStatusCodes": [
+ {
+ "code": "400"
+ },
+ {
+ "code": "403"
+ }
+ ]
+ },
+ {
+ "baseUrlType": "app",
+ "name": "workEvent",
+ "authType": "sso",
+ "url": "<% //Grab the relevant instance of the Work Event BO using the results of the getRequestId endpoint
+ 'workEvents/' + getRequestId.data[0].businessProcessTarget.id %>",
+ "exclude": false
+ },
+ {
+ "name": "getAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFiles?event=' + getRequestId.data[0].businessProcessTarget.id %>",
+ "authType": "sso"
+ }
+ ],
+ "presentation": {
+ "title": {
+ "label": "Work Event Details",
+ "type": "title"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "_comments": "Business Objects Details block",
+ "type": "section",
+ "horizontal": true,
+ "title": "Work Event Information",
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "fieldSet",
+ "children": [
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Work Event Name",
+ "value": "<% workEvent.name %>",
+ "enabled": false
+ },
+ {
+ "type": "richText",
+ "id": "description",
+ "label": "Description",
+ "value": "<% workEvent.description %>"
+ },
+ {
+ "type": "text",
+ "id": "location",
+ "label": "Proposed Event Location",
+ "value": "<% workEvent.location %>"
+ },
+ {
+ "type": "date",
+ "id": "date",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "Start Date of Work Event",
+ "value": "<% workEvent.startDate %>"
+ },
+ {
+ "type": "text",
+ "id": "sponsor",
+ "label": "Work Event Sponsor",
+ "value": "<% workEvent.sponsor %>"
+ },
+ {
+ "type": "currency",
+ "currencyCode": "USD",
+ "id": "cost",
+ "minimumValue": "<% 0.01 %>",
+ "fractionDigits": 2,
+ "label": "Fee (USD)",
+ "value": "<% workEvent.cost.value ?? 0 %>"
+ },
+ {
+ "type": "text",
+ "id": "contactInfo",
+ "label": "Contact Info/Email",
+ "required": "<% true %>",
+ "value": "<% workEvent.contactInfo %>"
+ },
+ {
+ "type": "text",
+ "id": "venue",
+ "label": "Work Event Venue",
+ "required": "<% true %>",
+ "value": "<% workEvent.venue.descriptor ?? '' %>"
+ },
+ {
+ "id": "internalOnlyCheckBox",
+ "type": "checkBox",
+ "value": "<% workEvent.internalOnly %>",
+ "visible": "<% true %>",
+ "label": "Internal Only Event?",
+ "enabled": false,
+ "trueLabel": "Yes",
+ "falseLabel": "No"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "fieldSet",
+ "title": "Work Event Attachments",
+ "render": "<% !(empty getAttachments.data) %>",
+ "children": [
+ {
+ "type": "fileUploader",
+ "id": "multiFileUploader",
+ "valueOutBinding": "postAttachments:filedata:",
+ "enabled": "false",
+ "fileTypes": "jpg,pdf",
+ "singular": false,
+ "children": [
+ {
+ "type": "loop",
+ "on": "<% getAttachments.data %>",
+ "as": "file",
+ "children": [
+ {
+ "type": "fileUploaderRow",
+ "render": "<% !(empty file.fileName) %>",
+ "attachment": {
+ "type": "attachmentList",
+ "attachmentURL": "<% 'https://api.workday.com/apps/' + site.applicationId + '/v1/attachedFiles/' + file.id %>",
+ "attachmentAuth": "sso",
+ "attachmentName": "<% file.fileName %>",
+ "attachmentUriTemplate": "[Target]",
+ "instanceList": [
+ {
+ "action": "attachment",
+ "descriptor": "<% file.fileName%>"
+ }
+ ]
+ },
+ "fileName": "<% file.fileName%>",
+ "fileType": "<% fileTypes.getMimeTypeIDbyContentType(file.contentType.id) %>"
+ }
+ ]
+ }
+ ],
+ "fileUploaderRowTemplate": [ ]
+ }
+ ]
+ },
+ {
+ "type": "button",
+ "label": "<% 'Return to Create a Work Event Home' %>",
+ "action": "PRIMARY",
+ "taskReference": {
+ "taskId": "eventHub"
+ }
+ }
+ ]
+ },
+ "footer": {
+ "type": "footer",
+ "children": [
+ {
+ "type": "richText",
+ "enabled": "false",
+ "value": "Powered By
Workday Extend 🎟️"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/workEventActionStep.pmd b/catalog/createAWorkEventWithAgent/presentation/workEventActionStep.pmd
new file mode 100644
index 0000000..5094603
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/workEventActionStep.pmd
@@ -0,0 +1,146 @@
+{
+ "id": "workEventActionStep",
+ "include": [
+ "fileTypes.script"
+ ],
+ "endPoints": [
+ {
+ "name": "bpEventStep",
+ "baseUrlType": "workday-bp",
+ "url": "<% //note the query param is mentioned in the pageRoute of the action step
+ //in the .businessprocess file --> /review/{eventStepId}. Use this syntax to grab the eventStepId path parameter
+ 'eventSteps/' + queryParams.eventStepId %>",
+ "authType": "sso"
+ },
+ {
+ "name": "eventInfo",
+ "baseUrlType": "workday-bp",
+ "url": "<% //this endpoint is calling the Workday Business Processes REST API for information about a specific BP Event for our Model BP
+ //use the event.id value from the bpEventStep response to get info about the Model BP Overall Event (overall process)
+ //bpEventStep.event would give us information about the entire BP Event (aka the overall process)
+ //not to be confused with the 'work event' Model BO we are acting on through our Model BP.
+ //and the bpEventStep.event.id grabs the ID of the BP's target BO, in this case our Model BO for a work event
+
+ 'events/' + bpEventStep.event.id %>",
+ "authType": "sso"
+ },
+ {
+ "name": "workEventInfo",
+ "baseUrlType": "app",
+ "url": "<% //this endpoint calls our app's Model BO endpoint for work events, and uses the ID of the work event retrieved above
+ //The ID of the Model BO in question is found in eventInfo['for'].id so calling this endpoint returns all the current Model BO data for that instace
+ 'workEvents/' + eventInfo['for'].id %>",
+ "authType": "sso"
+ },
+ {
+ "name": "userInfo",
+ "baseUrlType": "workday-common",
+ "url": "userInfo",
+ "authType": "sso"
+ },
+ {
+ "name": "me",
+ "baseUrlType": "workday-common",
+ "url": "workers/me",
+ "authType": "sso"
+ },
+ {
+ "name": "locationSearch",
+ "baseUrlType": "workday-wql",
+ "authType": "sso",
+ "url": "<% var query = 'SELECT workdayID as id, locationName as descriptor , subordinateLocation{workdayID as id, locationName as descriptor} FROM locations (locationUsagesForLocation = (fbe0f826480e46978f80b361d35f31df, 46db411c4a5245fbb39834d8b2cba049)) WHERE subordinateLocation is not empty';
+ console.info(query);
+ return 'data?query=' + query.urlEncode(); %>"
+ },
+ {
+ "name": "subLocationSearch",
+ "baseUrlType": "workday-wql",
+ "authType": "sso",
+ "url": "<% var query = 'SELECT subordinateLocation{workdayID as id, locationName as descriptor} FROM locations (locationUsagesForLocation = (fbe0f826480e46978f80b361d35f31df, 46db411c4a5245fbb39834d8b2cba049)) WHERE location=' + locationsInstanceList.promptSelections[0] + ' and subordinateLocation is not empty';
+ console.info(query);
+ return 'data?query=' + query.urlEncode(); %>",
+ "deferred": "true"
+ }
+ ],
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "workEventPATCH",
+ "baseUrlType": "app",
+ "url": "workEvents/<% eventInfo['for'].id %>",
+ "httpMethod": "PATCH",
+ "authType": "sso",
+ "onSend": "<% console.info(json:asJSON(self.data)); return self.data; %>"
+ },
+ {
+ "name": "submitStep",
+ "baseUrlType": "workday-bp",
+ "url": "eventSteps/<% queryParams.eventStepId %>/submit",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "onSend": "<% console.info(json:asJSON(self.data)); return self.data; %>",
+ "values": [
+ {
+ "outboundPath": "stepAction.id",
+ "value": "<% //this action page is ultimately just a PMD; when the information is submitted we need to
+ //advance the BP by calling the BP event with this exact value, which corresponds to the 'submit' step action ID
+ //this WID corrresponds to the step action ID of 'submitted'
+ 'd9e4223e446c11de98360015c5e6daf6' %>"
+ }
+ ]
+ }
+ ]
+ },
+ "presentation": {
+ "title": {
+ "label": "Work Event Request Details",
+ "type": "title"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "richText",
+ "value": "
",
+ "enabled": false
+ },
+ {
+ "type": "section",
+ "title": "This event has been approved. Please book an office location for the event and submit.",
+ "horizontal": false,
+ "children": [
+ {
+ "_comment": "Prompt for Location and the value selected will be passed",
+ "type": "instanceList",
+ "id": "locationsInstanceList",
+ "multiSelect": "false",
+ "label": "Select an Event Venue",
+ "required": "<% true %>",
+ "values": "<% locationSearch.data %>",
+ "monikerLevels": [
+ {
+ "type": "monikerLevel",
+ "levelValues": "<% subLocationSearch.data[0].subordinateLocation %>",
+ "endPoint": "<% endpoints.subLocationSearch %>"
+ }
+ ],
+ "valueOutBinding": "workEventPATCH.venue.id"
+ }
+ ]
+ }
+ ]
+ },
+ "footer": {
+ "type": "footer",
+ "children": [
+ {
+ "type": "richText",
+ "enabled": "false",
+ "value": "Powered By
Workday Extend 🎟️"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/workEventApprove.pmd b/catalog/createAWorkEventWithAgent/presentation/workEventApprove.pmd
new file mode 100644
index 0000000..5c33afa
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/workEventApprove.pmd
@@ -0,0 +1,198 @@
+{
+ "id": "workEventApprove",
+ "include": [
+ "fileTypes.script"
+ ],
+ "endPoints": [
+ {
+ "baseUrlType": "workday-wql",
+ "name": "getRequestId",
+ "authType": "sso",
+ "url": "<% //Grab the request ID using the BP Event ID. queryParams.eventId will automatically pull in the BP Event ID if it is in the page's scope
+ var query = 'SELECT businessProcessTarget FROM ' + site.applicationId + '_createWorkEventEvents WHERE workdayID = \"' + queryParams.eventId + '\"';
+ //if you reuse this code be sure to replace _createWorkEventEvents above with the appropriate endpoint for your Model BP
+ console.info(query);
+ return 'data?query=' + query.urlEncode() %>"
+ },
+ {
+ "baseUrlType": "app",
+ "name": "workEvent",
+ "authType": "sso",
+ "url": "<% //Grab the relevant instance of the Work Event BO using the results of the getRequestId endpoint
+ 'workEvents/' + getRequestId.data[0].businessProcessTarget.id %>",
+ "exclude": false
+ },
+ {
+ "name": "getAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFiles?event=' + getRequestId.data[0].businessProcessTarget.id %>",
+ "authType": "sso"
+ }
+ ],
+ "presentation": {
+ "title": {
+ "label": "Please Review a Work Event Submission",
+ "type": "title"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "_comments": "Business Objects Details block",
+ "type": "section",
+ "horizontal": true,
+ "title": "Work Event Information",
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "children": [
+ {
+ "type": "fieldSet",
+ "children": [
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Work Event Name",
+ "value": "<% workEvent.name %>",
+ "enabled": false
+ },
+ {
+ "type": "richText",
+ "id": "description",
+ "label": "Description",
+ "value": "<% workEvent.description %>"
+ },
+ {
+ "type": "text",
+ "id": "location",
+ "label": "Proposed Event Location",
+ "value": "<% workEvent.location %>"
+ },
+ {
+ "type": "date",
+ "id": "startdate",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "Start Date of Work Event",
+ "value": "<% workEvent.startDate %>"
+ },
+ {
+ "type": "date",
+ "id": "enddate",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "End Date of Work Event",
+ "value": "<% workEvent.endDate %>"
+ },
+ {
+ "type": "text",
+ "id": "sponsor",
+ "label": "Work Event Sponsor",
+ "value": "<% workEvent.sponsor %>"
+ },
+ {
+ "type": "currency",
+ "currencyCode": "USD",
+ "id": "cost",
+ "minimumValue": "<% 0.01 %>",
+ "fractionDigits": 2,
+ "label": "Fee (USD)",
+ "value": "<% workEvent.cost.value ?? 0 %>"
+ },
+ {
+ "type": "text",
+ "id": "contactInfo",
+ "label": "Contact Info/Email",
+ "required": "<% true %>",
+ "value": "<% workEvent.contactInfo %>"
+ },
+ {
+ "type": "text",
+ "id": "venue",
+ "label": "Work Event Venue",
+ "required": "<% true %>",
+ "value": "<% workEvent.venue.descriptor ?? '' %>"
+ },
+ {
+ "id": "internalOnlyCheckBox",
+ "type": "checkBox",
+ "value": "<% workEvent.internalOnly %>",
+ "visible": "<% true %>",
+ "label": "Internal Only Event?",
+ "enabled": false,
+ "trueLabel": "Yes",
+ "falseLabel": "No"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "fieldSet",
+ "title": "Work Event Attachments",
+ "render": "<% !(empty getAttachments.data) %>",
+ "children": [
+ {
+ "type": "fileUploader",
+ "id": "multiFileUploader",
+ "valueOutBinding": "postAttachments:filedata:",
+ "enabled": "false",
+ "fileTypes": "jpg,pdf",
+ "singular": false,
+ "children": [
+ {
+ "type": "loop",
+ "on": "<% getAttachments.data %>",
+ "as": "file",
+ "children": [
+ {
+ "type": "fileUploaderRow",
+ "render": "<% !(empty file.fileName) %>",
+ "attachment": {
+ "type": "attachmentList",
+ "attachmentURL": "<% 'https://api.workday.com/apps/' + site.applicationId + '/v1/attachedFiles/' + file.id %>",
+ "attachmentAuth": "sso",
+ "attachmentName": "<% file.fileName %>",
+ "attachmentUriTemplate": "[Target]",
+ "instanceList": [
+ {
+ "action": "attachment",
+ "descriptor": "<% file.fileName%>"
+ }
+ ]
+ },
+ "fileName": "<% file.fileName%>",
+ "fileType": "<% fileTypes.getMimeTypeIDbyContentType(file.contentType.id) %>"
+ }
+ ]
+ }
+ ],
+ "fileUploaderRowTemplate": [ ]
+ }
+ ]
+ },
+ {
+ "type": "button",
+ "label": "<% 'Return to Create a Work Event Home' %>",
+ "action": "PRIMARY",
+ "taskReference": {
+ "taskId": "eventHub"
+ }
+ }
+ ]
+ },
+ "footer": {
+ "type": "footer",
+ "children": [
+ {
+ "type": "richText",
+ "enabled": "false",
+ "value": "Powered By
Workday Extend 🎟️"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/createAWorkEventWithAgent/presentation/workEventRevise.pmd b/catalog/createAWorkEventWithAgent/presentation/workEventRevise.pmd
new file mode 100644
index 0000000..1caf1e6
--- /dev/null
+++ b/catalog/createAWorkEventWithAgent/presentation/workEventRevise.pmd
@@ -0,0 +1,307 @@
+{
+ "id": "workEventRevise",
+ "endPoints": [
+ {
+ "name": "me",
+ "baseUrlType": "workday-staffing",
+ "url": "workers/me",
+ "authType": "sso"
+ },
+ {
+ "name": "bpEventStep",
+ "baseUrlType": "workday-bp",
+ "url": "<% //note the query param is mentioned in the pageRoute of the revise step
+ //in the .businessprocess file --> /revise/{eventStepId}. Use this syntax to grab the eventStepId path parameter
+ 'eventSteps/' + queryParams.eventStepId %>",
+ "authType": "sso"
+ },
+ {
+ "name": "eventInfo",
+ "baseUrlType": "workday-bp",
+ "url": "<% //this endpoint is calling the Workday Business Processes REST API for information about a specific BP Event for our Model BP
+ //use the event.id value from the bpEventStep response to get info about the Model BP Overall Event (overall process)
+ //bpEventStep.event would give us information about the entire BP Event (aka the overall process)
+ //not to be confused with the 'work event' Model BO we are acting on through our Model BP.
+ //and the bpEventStep.event.id grabs the ID of the BP's target BO, in this case our Model BO for a work event
+
+ 'events/' + bpEventStep.event.id %>",
+ "authType": "sso"
+ },
+ {
+ "name": "workEventInfo",
+ "baseUrlType": "app",
+ "url": "<% //this endpoint calls our app's Model BO endpoint for work events, and uses the ID of the work event retrieved above
+ //The ID of the Model BO in question is found in eventInfo['for'].id so calling this endpoint returns all the current Model BO data for that instace
+ 'workEvents/' + eventInfo['for'].id %>",
+ "authType": "sso"
+ },
+ {
+ "name": "getAttachments",
+ "baseUrlType": "app",
+ "url": "<% 'attachedFiles?event=' + workEventInfo.id %>",
+ "authType": "sso"
+ },
+ {
+ "name": "getEventComments",
+ "url": "<% 'events/' + bpEventStep.event.id + '/comments' %>",
+ "authType": "sso",
+ "baseUrlType": "workday-bp",
+ "failOnStatusCodes": [
+ {
+ "code": "400"
+ },
+ {
+ "code": "403"
+ }
+ ]
+ }
+ ],
+ "outboundData": {
+ "outboundEndPoints": [
+ {
+ "name": "updateEvent",
+ "baseUrlType": "app",
+ "url": "<% 'workEvents/' + workEventInfo.id %>",
+ "authType": "sso",
+ "httpMethod": "PUT",
+ "allowPutForUnchangedData": true,
+ "onSend": "<% //the above allowPutForUnchangedData attribute will cause the PUT to fire upon page submission even if no data appears to have changed
+ //Outbound endpoint that updates the BO. We need to provide all existing values on the Event so they do not fall off since this is a PUT
+ //it is always preferable to add to your payload using onSend as opposed to a values[] array within the outboundEndpoint. Use the following syntax.
+ //self.data is the payload as it exists coming into this onSend event
+ self.data.internalOnly = internalOnlyRadioGroup.value[0] == 'yes' ? true : false; //convert internalOnly radio group response from String to Boolean
+ self.data.description = description.value;
+ self.data.put('createdBy',{'id': workEventInfo.createdBy.id}); //add createdBy object
+ console.info(json:asJSON(self.data)); return self.data; %>"
+ },
+ {
+ "name": "eventAttachment",
+ "baseUrlType": "app",
+ "url": "attachedFiles",
+ "authType": "sso",
+ "httpMethod": "POST",
+ "multiPartFileLast": true,
+ "onSend": "<% //onSend not sent for multipart! yet :) thanks @shuo for confirming
+ //logs the eventImage payload to the console and then submits the request
+ //self.data.put('file', 'cid:attachment'); //add Content ID to data part of multipart request
+ // self.data.put('event', {'id':workEventInfo.id}); //add Event BO ID to data part of multipart request; this links the attachment to the BO
+ //self.data.put('uploadedBy', {'id':me.id});
+ //self.data.put('uploadedDate', date:getTodaysDate(null));
+ //outboundPath is only the JSON path within a part; can't navigate into a part. nowhere to enter a part name
+ //valueOutBinding is only place to enter a part name
+ console.info('eventAttachment metadata: ' + json:asJSON(self.data));
+ return self.data;
+ %>",
+ "exclude": "<% //This exclude statement checks whether a file was uploaded successfully!
+ empty attachmentFileUploader.children %>"
+ },
+ {
+ "name": "submitStep",
+ "baseUrlType": "workday-bp",
+ "url": "eventSteps/<% queryParams.eventStepId %>/submit",
+ "httpMethod": "POST",
+ "authType": "sso",
+ "onSend": "<% console.info(json:asJSON(self.data)); return self.data; %>",
+ "values": [
+ {
+ "outboundPath": "stepAction.id",
+ "value": "<% //this action page is ultimately just a PMD; when the information is submitted we need to
+ //advance the BP by calling the BP event with this exact value, which corresponds to the 'submit' step action ID
+ //this WID corrresponds to the step action ID of 'submitted'
+ 'd9e4223e446c11de98360015c5e6daf6' %>"
+ }
+ ]
+ }
+ ],
+ "responseErrorDetail": {
+ "errorSummary": "<% error %>",
+ "errors": "<% errors.map(item => { item.error }); %>"
+ }
+ },
+ "presentation": {
+ "title": {
+ "type": "title",
+ "label": "Update Event"
+ },
+ "headerSize": "STANDARD",
+ "body": {
+ "type": "section",
+ "horizontal": true,
+ "children": [
+ {
+ "type": "section",
+ "id": "leftside",
+ "children": [
+ {
+ "type": "text",
+ "id": "sendBackComment",
+ "label": "<% 'Send Back Reason from ' + getEventComments.data[0].person.descriptor + ':'%>",
+ "value": "<% getEventComments.data[0].comment %>",
+ "enabled": false
+ },
+ {
+ "type": "text",
+ "id": "name",
+ "label": "Event Name",
+ "required": "<% true %>",
+ "value": "<% workEventInfo.name %>",
+ "valueOutBinding": "updateEvent.name"
+ },
+ {
+ "type": "richText",
+ "id": "description",
+ "label": "Description",
+ "required": "<% true %>",
+ "value": "<% workEventInfo.description %>"
+ },
+ {
+ "type": "text",
+ "id": "location",
+ "label": "Event Location",
+ "required": "<% true %>",
+ "value": "<% workEventInfo.location %>",
+ "valueOutBinding": "updateEvent.location"
+ },
+ {
+ "type": "date",
+ "id": "startDate",
+ "required": "<% true %>",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "Start Date of Event",
+ "value": "<% workEventInfo.startDate %>",
+ "valueOutBinding": "updateEvent.startDate"
+ },
+ {
+ "type": "date",
+ "id": "endDate",
+ "required": "<% true %>",
+ "dateFormat": "yyyy-MM-dd",
+ "label": "End Date of Event",
+ "value": "<% workEventInfo.endDate %>",
+ "valueOutBinding": "updateEvent.endDate"
+ },
+ {
+ "type": "text",
+ "id": "sponsor",
+ "label": "Event Sponsor",
+ "value": "<% workEventInfo.sponsor %>",
+ "valueOutBinding": "updateEvent.sponsor"
+ },
+ {
+ "type": "currency",
+ "currencyCode": "USD",
+ "id": "cost",
+ "fractionDigits": 2,
+ "label": "Fee (USD)",
+ "value": "<% //the value to display on page load, in this case the existing value from the inbound endpoint
+ workEventInfo.cost.value ?? 0 %>",
+ "valuesOut": [
+ {
+ "value": "USD",
+ "valueOutBinding": "updateEvent.cost.currency"
+ },
+ {
+ "value": "<% //the value the user enters, whereas the valueOutBinding below is what will hit the outbound endpoint
+ self.value %>",
+ "valueOutBinding": "updateEvent.cost.value"
+ }
+ ]
+ },
+ {
+ "type": "text",
+ "id": "contactInfo",
+ "label": "Contact Info/Email",
+ "value": "<% workEventInfo.contactInfo %>",
+ "valueOutBinding": "updateEvent.contactInfo"
+ },
+ {
+ "type": "radioGroup",
+ "id": "internalOnlyRadioGroup",
+ "label": "Is this a Workday Extend only event?",
+ "hideDisplayOption": "true",
+ "required": "true",
+ "selectedValues": "<% ! empty workEventInfo.internalOnly ? (workEventInfo.internalOnly ? ['yes'] : ['no'] ) : ['no'] %>",
+ "instanceList": [
+ {
+ "id": "yes",
+ "descriptor": "Yes"
+ },
+ {
+ "id": "no",
+ "descriptor": "No"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "id": "rightside",
+ "children": [
+ {
+ "type": "section",
+ "horizontal": false,
+ "sorted": false,
+ "children": [
+ {
+ "type": "fileUploader",
+ "label": "Attach Files Related to the Event",
+ "id": "attachmentFileUploader",
+ "maxImageDimension": "1024",
+ "maxFileSize": "5242880",
+ "compact": "<% true %>",
+ "required": "<% false %>",
+ "fileTypes": "*",
+ "valueOutBinding": "<% 'createEventAttachment:attachedFiles' //outbound endpoint name : attachment obect API URL
+ %> ",
+ "metadata": "<%
+ {:} //need at least an empty map in a metadata tag for onMultiPartSend() to have metadata to work with!
+ %>"
+ }
+ ]
+ },
+ {
+ "type": "fileUploader",
+ "label": "Previously attached files for Event:",
+ "id": "multiFileUploader",
+ "valueOutBinding": "postAttachments:filedata:",
+ "enabled": "false",
+ "fileTypes": "txt,jpg,jpeg,png,ppt,pptx,pdf,doc,docx,xlsx,xls",
+ "singular": false,
+ "children": [
+ {
+ "type": "loop",
+ "on": "<% getAttachments.data %>",
+ "as": "file",
+ "children": [
+ {
+ "type": "fileUploaderRow",
+ "render": "<% !(empty file.fileName) %>",
+ "attachment": {
+ "type": "attachmentList",
+ "attachmentURL": "<% 'https://api.workday.com/apps/' + site.applicationId + '/v1/attachedFiles/' + file.id %>",
+ "attachmentAuth": "sso",
+ "attachmentName": "<% file.fileName %>",
+ "attachmentUriTemplate": "[Target]",
+ "instanceList": [
+ {
+ "action": "attachment",
+ "descriptor": "<% file.fileName%>",
+ "mimeType": "<% fileType:getFileType(file.fileName) %>"
+ }
+ ]
+ },
+ "fileName": "<% file.fileName%>",
+ "fileType": "<% fileType:getFileType(file.fileName) %>"
+ }
+ ]
+ }
+ ],
+ "fileUploaderRowTemplate": [ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file