Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/assets/images/mobile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/assets/images/org.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/assets/images/static.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions app/assets/javascript/map-scripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
function circleIcon({ fill = '#fff', stroke = '#000', strokeWidth = 2,
text = '', textColor = '#000', radius = 14, fillOpacity = 1 } = {}) {
const width = radius * 2;
const pointHeight = radius * 1.2;
const height = width + pointHeight;
const cx = radius;
const cy = radius;

return L.divIcon({
html: `<svg viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
<!-- Stroke-only pin line -->
<line
x1="${cx}" y1="${cy + radius - strokeWidth}"
x2="${cx}" y2="${height - strokeWidth}"
stroke="${stroke}" stroke-width="${strokeWidth}"/>
<!-- Filled circle on top -->
<circle
cx="${cx}" cy="${cy}" r="${radius - strokeWidth}"
fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="${strokeWidth}"/>
<!-- Text -->
<text x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central"
fill="${textColor}" font-size="${radius * 0.9}" font-family="sans-serif" font-weight="bold">
${text}
</text>
</svg>`,
className: '',
iconSize: [width, height],
iconAnchor: [cx, height],
popupAnchor: [0, -height]
});
}

const landmarkIcons = {
orgLocation: circleIcon({ fill: '#fff9c4', text: 'O' }),
static: circleIcon({ fill: '#fff9c4', text: 'S' }),
mobile: circleIcon({ fill: '#ed8b00', text: 'M' }),
default: circleIcon({ fill: '#fff', text: '' }),
};

const mapOptions = {
attributionControl: false,
scrollWheelZoom: false
};

const map = L.map('app-map', {
maxZoom: 15
});
96 changes: 96 additions & 0 deletions app/assets/sass/components/_secondary-navigation.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
@use "nhsuk-frontend/dist/nhsuk/core" as *;

.app-secondary-navigation {
margin-right: #{$nhsuk-gutter-half * -1};
margin-left: #{$nhsuk-gutter-half * -1};

@include nhsuk-responsive-margin(5, "bottom");

@include nhsuk-media-query($from: tablet) {
margin-right: auto;
margin-left: auto;
}
}

.app-secondary-navigation__link {
display: flex;
align-items: center;
gap: 6px;
padding: nhsuk-spacing(2) $nhsuk-gutter-half;

&:link {
text-decoration: none;
}

@include nhsuk-link-style-default;
@include nhsuk-link-style-no-visited-state;

@include nhsuk-media-query($from: tablet) {
padding: nhsuk-spacing(3) 2px;
}

&[aria-current] {
color: $nhsuk-text-colour;
box-shadow: inset $nhsuk-border-width 0 nhsuk-colour("blue");
text-decoration: none;

@include nhsuk-media-query($from: tablet) {
box-shadow: inset 0 ($nhsuk-border-width * -1) nhsuk-colour("blue");
}
}

&:focus {
box-shadow: inset $nhsuk-focus-width 0 $nhsuk-focus-text-colour;

@include nhsuk-media-query($from: tablet) {
box-shadow: inset 0 ($nhsuk-focus-width * -1) $nhsuk-focus-text-colour;
}
}

.nhsuk-icon {
width: 1.5rem;
height: 1.5rem;
}
}

.app-secondary-navigation__link--disabled {
color: $nhsuk-secondary-text-colour;
cursor: not-allowed;
pointer-events: none;
text-decoration: none;

&:hover,
&:focus {
text-decoration: none;
}
}

.app-secondary-navigation__current {
font-weight: inherit;
}

.app-secondary-navigation__list {
display: flex;

flex-flow: column;

width: 100%;
margin: 0;
padding: 0;

list-style: none;
// The list uses box-shadow rather than a border to set a 1px grey line at the
// bottom, so that the current item appears on top of the grey line.
box-shadow: inset 0 -1px 0 $nhsuk-border-colour;

@include nhsuk-font(19);

@include nhsuk-media-query($from: tablet) {
flex-flow: row wrap;
gap: nhsuk-spacing(2) nhsuk-spacing(5);
}
}

.app-secondary-navigation__list-item {
margin-bottom: 0;
}
12 changes: 11 additions & 1 deletion app/assets/sass/main.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
// Import NHS.UK frontend library
@import "nhsuk-frontend/dist/nhsuk";
@forward "nhsuk-frontend/dist/nhsuk/nhsuk";

// Stolen Manage prototype components :P
// https://github.com/NHSDigital/manage-breast-screening-prototype

@forward "components/secondary-navigation";

// Add your custom CSS/Sass styles below.
.app-card-editable {
display: flex;
justify-content: space-between;
align-items: baseline;
}
2 changes: 1 addition & 1 deletion app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module.exports = {
// Service name
serviceName: 'Service name goes here',
serviceName: 'Cohort to clinic service name',

// Port to run the prototype on locally
port: 3000
Expand Down
89 changes: 45 additions & 44 deletions app/filters.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,45 @@
/**
* @param {Environment} env
*/
module.exports = function (env) {
const filters = {}

/* ------------------------------------------------------------------
add your methods to the filters obj below this comment block:
@example:

filters.sayHi = function(name) {
return 'Hi ' + name + '!'
}

Which in your templates would be used as:

{{ 'Paul' | sayHi }} => 'Hi Paul'

Notice the first argument of your filters method is whatever
gets 'piped' via '|' to the filter.

Filters can take additional arguments, for example:

filters.sayHi = function(name,tone) {
return (tone == 'formal' ? 'Greetings' : 'Hi') + ' ' + name + '!'
}

Which would be used like this:

{{ 'Joel' | sayHi('formal') }} => 'Greetings Joel!'
{{ 'Gemma' | sayHi }} => 'Hi Gemma!'

For more on filters and how to write them see the Nunjucks
documentation.

------------------------------------------------------------------ */

/* keep the following line to return your filters to the app */
return filters
}

/**
* @import { Environment } from 'nunjucks'
*/
// app/filters.js

const fs = require('fs')
const path = require('path')

module.exports = function (env) {
/* eslint-disable-line func-names,no-unused-vars */
/**
* Instantiate object used to store the methods registered as a
* 'filter' (of the same name) within nunjucks. You can override
* gov.uk core filters by creating filter methods of the same name.
*
* @type {object}
*/
const filters = {}

// Get all files from utils directory
const utilsPath = path.join(__dirname, 'lib/utils')
//const filtersPath = path.join(__dirname, 'filters')

//const folderPaths = [utilsPath, filtersPath]
const folderPaths = [utilsPath]

try {
folderPaths.forEach((folderPath) => {
const files = fs.readdirSync(folderPath)

files.forEach((file) => {
if (path.extname(file) === '.js') {
const module = require(path.join(folderPath, file))

Object.entries(module).forEach(([name, func]) => {
if (typeof func === 'function') {
filters[name] = func
}
})
}
})
})
} catch (err) {
console.warn('Error loading filters:', err)
}

return filters
}
Loading