Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Choose landing page from templates #48

Merged
merged 12 commits into from
Oct 30, 2023
81 changes: 81 additions & 0 deletions resources/instructions/createSObjectLwcQuickActions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!doctype html>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This one's just a mock so far. It won't be called in the current workflow.

<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Offline Starter Kit: Create sObject LWC Quick Actions</title>
</head>
<style>
h1 {
font-size: 16px;
}
h2 {
font-size: 14px;
}
table {
border-collapse: collapse;
}
table,
th,
td {
border-width: 1px;
border-style: solid;
}
th,
td {
text-align: center;
}
</style>
<body>
<h1>Create sObject LWC Quick Actions</h1>

<p>
The following sObjects are present in your configured landing page:
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Probably standardize on SObjects? I'm not sure which capitalization is actually preferred but SObject seems to be used throughout.

Copy link
Collaborator Author

@khawkins khawkins Oct 27, 2023

Choose a reason for hiding this comment

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

I had done it as a distinction between title case (headings) and lower case (in sentences). But I think for title case, I erroneously looked at the SObject class in the Apex Reference Guide for guidance, which is likely taking casing cues based more on coding standards than nomenclature.

The Use sObjects trail in Trailhead seems to treat it as "sObject" in all cases. So that's probably the one worth standardizing on. Good callout!

</p>

<table>
<thead>
<tr>
<th>sObject</th>
<th>LWC Quick Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>Contact</td>
<td>✅</td>
</tr>
<tr>
<td>Account</td>
<td>✅</td>
</tr>
<tr>
<td>Opportunity</td>
<td>❌</td>
</tr>
</tbody>
</table>

<label
style="display: block; margin-top: 10px; margin-bottom: 8px"
for="generateLwcQuickActionsButton"
>
Click 'Generate LWC Quick Actions' to create the missing LWC Quick
Actions:
</label>
<button id="generateLwcQuickActionsButton">
Generate LWC Quick Actions
</button>
<script>
const generateQuickActionsButtonElement = document.getElementById(
'generateLwcQuickActionsButton'
);
generateQuickActionsButtonElement.addEventListener('click', () => {
webviewMessaging.sendMessageRequest(
'generateLwcQuickActionsButton'
);
});
</script>
<script src="--- MESSAGING_SCRIPT_SRC ---"></script>
</body>
</html>
262 changes: 262 additions & 0 deletions resources/instructions/landingPageTemplateChoice.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Offline Starter Kit: Select Landing Page</title>
<style>
.landing-page-option-set {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.landing-page-option-title {
margin: 0px;
font-weight: bold;
font-size: 12px;
}
.landing-page-option-subtitle {
margin: 0px;
font-size: 10px;
}
h1 {
font-size: 18px;
}
.warning,
.error {
color: #f00;
font-size: 12px;
margin: 3px 5px;
}
.enabled-content-toggle.disabled {
opacity: 0.25;
}
#globalError,
#chooseTemplateButton {
margin-top: 12px;
margin-bottom: 12px;
}
</style>
</head>
<body>
<h1>Select a Landing Page Template</h1>

<p id="globalError" class="error" style="display: none"></p>
<div id="landingPageCollection" class="enabled-content-toggle">
<label id="existingLandingPageTemplateLabel">
<div class="enabled-content-toggle landing-page-option-set">
<input
type="radio"
id="existingLandingPage"
name="landingPageOption"
value="existing"
/>
<div>
<p class="landing-page-option-title">Keep Existing</p>
<p class="landing-page-option-subtitle">
Keep the landing page that's already configured in
your project.
</p>
</div>
</div>
</label>

<label id="defaultLandingPageTemplateLabel">
Copy link
Collaborator

Choose a reason for hiding this comment

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

So adding new landing pages in the future will require copy-pasting the html snippets here in this file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

At the moment. But it's a good point: all of these entries could be dynamically generated based on returned data—which they already get, to drive their visibility.

I had thought about that approach, and at the end of the day wasn't sure how much the entries in this page would fluctuate, to make it worthwhile to drive the content dynamically. We're well positioned to do so though, if we decide to go that route.

<p
id="defaultLandingPageWarning"
class="warning"
style="display: none"
></p>
<div class="enabled-content-toggle landing-page-option-set">
<input
type="radio"
id="defaultLandingPage"
name="landingPageOption"
value="default"
/>
<div>
<p class="landing-page-option-title">Default</p>
<p class="landing-page-option-subtitle">
Recently viewed Contacts, Accounts, and
Opportunities
</p>
</div>
</div>
</label>

<label id="caseManagementLandingPageTemplateLabel">
<p
id="caseManagementLandingPageWarning"
class="warning"
style="display: none"
></p>
<div class="enabled-content-toggle landing-page-option-set">
<input
type="radio"
id="caseManagementLandingPage"
name="landingPageOption"
value="caseManagement"
/>
<div>
<p class="landing-page-option-title">Case Management</p>
<p class="landing-page-option-subtitle">
New Case action and the 5 most recent Cases,
Accounts, and Contacts
</p>
</div>
</div>
</label>

<label id="healthcareLandingPageTemplateLabel">
<p
id="healthcareLandingPageWarning"
class="warning"
style="display: none"
></p>
<div class="enabled-content-toggle landing-page-option-set">
<input
type="radio"
id="healthcareLandingPage"
name="landingPageOption"
value="healthcare"
/>
<div>
<p class="landing-page-option-title">Healthcare</p>
<p class="landing-page-option-subtitle">
Global quick actions with BarcodeScanner, new
Visitor, and more
</p>
</div>
</div>
</label>

<label id="retailLandingPageTemplateLabel">
<p
id="retailLandingPageWarning"
class="warning"
style="display: none"
></p>
<div class="enabled-content-toggle landing-page-option-set">
<input
type="radio"
id="retailLandingPage"
name="landingPageOption"
value="retail"
/>
<div>
<p class="landing-page-option-title">
Retail Execution
</p>
<p class="landing-page-option-subtitle">
Global quick actions with new Opportunity, new Lead,
and more
</p>
</div>
</div>
</label>
</div>
<button id="chooseTemplateButton">Choose Template</button>
<script>
// Wait until all scripts are loaded, before engaging with e.g.
// messaging functionality.
window.addEventListener('load', () => {
// What's the status of the various landing pages / templates?
webviewMessaging.sendMessageRequest(
'landingPageStatus',
{},
handleLandingPageStatusResponse
);

// chooseTemplateButton click
const chooseTemplateButtonElement = document.getElementById(
'chooseTemplateButton'
);
chooseTemplateButtonElement.addEventListener(
'click',
chooseTemplateClicked
);
});

function chooseTemplateClicked() {
const selectedTemplateElement = document.querySelector(
'input[name="landingPageOption"]:checked'
);
if (!selectedTemplateElement) {
const globalErrorElement =
document.getElementById('globalError');
globalErrorElement.innerText = 'No landing page selected.';
globalErrorElement.style.display = 'block';
} else {
const selectedValue = selectedTemplateElement.value;
webviewMessaging.sendMessageRequest('landingPageChosen', {
landingPageType: selectedValue
});
}
}

function handleLandingPageStatusResponse(response) {
if (response.error) {
const globalErrorElement =
document.getElementById('globalError');
globalErrorElement.innerText = response.error;
globalErrorElement.style.display = 'block';
const landingPageCollectionElement =
document.getElementById('landingPageCollection');
landingPageCollectionElement.classList.add('disabled');
const radioElements = document.querySelectorAll(
".enabled-content-toggle > input[type='radio']"
);
radioElements.forEach((radioElement) => {
radioElement.disabled = true;
});
const chooseTemplateButton = document.getElementById(
'chooseTemplateButton'
);
chooseTemplateButton.disabled = true;
} else {
for (const landingPageType in response.landingPageCollection) {
const landingPageStatus =
response.landingPageCollection[landingPageType];
if (!landingPageStatus.exists) {
const landingPageLabelId = `${landingPageType}LandingPageTemplateLabel`;
const landingPageLabelElement =
document.getElementById(landingPageLabelId);
if (landingPageStatus.warning) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not understanding what this is for as I dont see any warning text defined anywhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The warning data comes back as one of the status data items from the TemplateChooserCommand.getLandingPageStatus() method. That status check will create a warning if one or both of the landing page files for the given template are not available in the project.

const warningElement =
landingPageLabelElement.querySelector(
'p.warning'
);
// We'll leave the warning element off of the existing landing page,
// for example.
if (warningElement) {
warningElement.innerText =
landingPageStatus.warning;
warningElement.style.display = 'block';
}
}
const contentToDisableElement =
landingPageLabelElement.querySelector(
'.enabled-content-toggle'
);
contentToDisableElement.classList.add('disabled');
const radioButtonElement =
contentToDisableElement.querySelector(
"input[type='radio']"
);
radioButtonElement.disabled = true;
}
}

// If there's an existing landing page, make that the selected.
if (response.landingPageCollection['existing'].exists) {
const existingRadioElement = document.getElementById(
'existingLandingPage'
);
existingRadioElement.checked = true;
}
}
}
</script>
<script src="--- MESSAGING_SCRIPT_SRC ---"></script>
</body>
</html>
2 changes: 1 addition & 1 deletion src/commands/wizard/landingPageCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class LandingPageCommand {
}

/**
* A Record List card shows a customized card for a particular SObject. It needs the following params from the user:
* A Record List card shows a customized card for a particular sObject. It needs the following params from the user:
* - Primary, and optionally Secondary and Tertiary fields
* - OrderBy field
* - OrderBy direction (Ascending or Descending)
Expand Down
30 changes: 30 additions & 0 deletions src/commands/wizard/lwcGenerationCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Uri, l10n } from 'vscode';
import { InstructionsWebviewProvider } from '../../webviews/instructions';

export class LwcGenerationCommand {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Another one that's just stubbed. Not accessible to the user yet.

extensionUri: Uri;

constructor(extensionUri: Uri) {
this.extensionUri = extensionUri;
}

async createSObjectLwcQuickActions() {
return new Promise<void>((resolve) => {
new InstructionsWebviewProvider(
this.extensionUri
).showInstructionWebview(
l10n.t('Offline Starter Kit: Create sObject LWC Quick Actions'),
'resources/instructions/createSObjectLwcQuickActions.html',
[
{
type: 'generateLwcQuickActionsButton',
action: (panel) => {
panel.dispose();
return resolve();
}
}
]
);
});
}
}
Loading