Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4937edd7f4 | ||
|
|
fcba390804 | ||
|
|
8b6e3e60bc | ||
|
|
d244a34fc9 | ||
|
|
686a745f0b | ||
|
|
510947d173 | ||
|
|
589a745c5b | ||
|
|
d04c55c731 | ||
|
|
a6a6abf009 | ||
|
|
88368fa31d | ||
|
|
fdb1db216d | ||
|
|
6d833f9654 | ||
|
|
6bcd0adf81 | ||
|
|
93348ad604 | ||
|
|
7561bb0c39 | ||
|
|
959a9ffab4 | ||
|
|
2348575737 | ||
|
|
b2aac21b90 | ||
|
|
438b1e93d7 | ||
|
|
674737f840 | ||
|
|
150be89246 |
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
title: 'Full Table Search: Find Any Value in Any Table'
|
||||||
|
description: Type a value into the search box and Data Controller scans every column for it - no query, no WHERE clause.
|
||||||
|
date: '2026-09-15 17:00:00'
|
||||||
|
author: 'Data Controller'
|
||||||
|
authorLink: https://www.linkedin.com/showcase/data-controller-for-sas
|
||||||
|
tags:
|
||||||
|
- Announcements
|
||||||
|
previewImg: './full-table-search.jpeg'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Full Table Search: Find Any Value in Any Table
|
||||||
|
|
||||||
|
Finding a value in a large table usually means writing a query first: guess which column it lives in, write a WHERE clause, run it, and try again when you guess wrong. Data Controller's **full table search** removes that step. Choose a library and table in the Viewer, type a value into the search box and press Enter - every column in the table is scanned for it, and the matching rows come straight back into the grid.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
The search box sits in the Viewer toolbar, next to a **Numeric** checkbox:
|
||||||
|
|
||||||
|
- **Text search** matches part of a value using the case sensitive SAS® `CONTAINS` operator, so `smith` finds `Goldsmith` but not `Smithson`.
|
||||||
|
- **Numeric search** (tick the box) matches the number exactly against every numeric column in the table.
|
||||||
|
|
||||||
|
Whichever you use, the results are ordinary rows in the Viewer, with the rest of the screen behaving exactly as it does for a normal view.
|
||||||
|
|
||||||
|
## Any database, not just SAS datasets
|
||||||
|
|
||||||
|
The scan runs inside SAS, against whichever libname engine the table is assigned to - so it is not limited to SAS datasets.
|
||||||
|
|
||||||
|
## Built on open source
|
||||||
|
|
||||||
|
This feature, like most of Data Controller, is built on the [SASjs Macro Core](https://github.com/sasjs/core) library, using the [`%mp_searchdata()`](https://core.sasjs.io/mp__searchdata_8sas.html) macro. The macro assembles a single DATA step with one `OR` clause per column - a `CONTAINS` test for character columns, an equality test for numeric ones - and writes out only the matching records. It is MIT licensed, so you can read, test and audit the code that is running against your data.
|
||||||
|
|
||||||
|
A few things worth knowing:
|
||||||
|
|
||||||
|
- The search respects your current filter, Row Level Security and Column Level Security - it scans the view you are entitled to see, not the raw table.
|
||||||
|
- Full table search is available in ViewBoxes as well, so the related tables lined up beside your main grid can be searched the same way.
|
||||||
|
- Nothing is shipped to an external index or search service: the scan happens on your own SAS platform.
|
||||||
|
|
||||||
|
See it in action:
|
||||||
|
|
||||||
|
<iframe title="Full Table Search" width="560" height="315" src="https://vid.4gl.io/videos/embed/qdEv4PP2oiPr5VXwLbN58B" style="border: 0px;" allow="fullscreen" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"></iframe>
|
||||||
|
|
||||||
|
More detail in the [Viewer documentation](https://docs.datacontroller.io/dcu-tableviewer/).
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Source LinkedIn post:
|
||||||
|
|
||||||
|
Data Controller for SAS® has a plethora of features to make Data Discovery easier
|
||||||
|
|
||||||
|
Here we demonstrate "full table search". No need to formulate a query - just type a value and hit enter!
|
||||||
|
|
||||||
|
It works on all databases
|
||||||
|
|
||||||
|
This feature, like most others, is built on our open-source #SASjs library - using the `mp_searchdata()` macro (https://lnkd.in/gxZZq76j).
|
||||||
|
|
||||||
|
#sas #sasapps #sasviya
|
||||||
|
|
||||||
|
video: https://vid.4gl.io/w/qdEv4PP2oiPr5VXwLbN58B
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Image prompt:
|
||||||
|
Flat vector illustration on a dark slate (#314351) background with a faint dot grid: a stylised data table across the lower half (rounded header bar plus eight rows of rounded cells), with the fourth row highlighted in brand green (#90c445). A large magnifying glass with a thick green rim and diagonal handle overlaps the table on the right, and inside its dark lens the highlighted row appears magnified with two white value blocks. Headline "FULL TABLE SEARCH" in bold white uppercase, subheading "any value, every column - no query required" in brand green. 1200x627 landscape, no other text. Composed programmatically with PIL in the Data Controller brand palette rather than generated by an image model.
|
||||||
|
-->
|
||||||
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 51 KiB |
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
title: 'If: An Ode to Data Control'
|
||||||
|
description: Rudyard Kipling's 'If', reworked for the data governance era - the full text of a poem first published in 2022.
|
||||||
|
date: '2026-09-20 15:00:00'
|
||||||
|
author: 'Allan Bowe'
|
||||||
|
authorLink: https://www.linkedin.com/in/allanbowe/
|
||||||
|
tags:
|
||||||
|
- Announcements
|
||||||
|
previewImg: './if-an-ode-to-data-control.jpeg'
|
||||||
|
---
|
||||||
|
|
||||||
|
# If: An Ode to Data Control
|
||||||
|
|
||||||
|
Rudyard Kipling wrote "If" in 1895 and published it in his 1910 collection Rewards and Fairies. This ode to data control was first published in 2022. Here is the full text.
|
||||||
|
|
||||||
|
```poem
|
||||||
|
If you can keep your data when all about you
|
||||||
|
Are losing theirs and blaming it on you;
|
||||||
|
If you can trust your metrics when analysts doubt you,
|
||||||
|
But make allowance for their doubting too:
|
||||||
|
|
||||||
|
If you can query and not be tired by waiting,
|
||||||
|
Or building models, adjust for outliers,
|
||||||
|
Define ETL modules that are self-validating,
|
||||||
|
And require contracts from data suppliers;
|
||||||
|
|
||||||
|
|
||||||
|
If you can report - and not make KPIs your master;
|
||||||
|
If you can forecast - and not make compliance your aim,
|
||||||
|
If you can make the overnight batch faster
|
||||||
|
And ensure the end results are just the same:
|
||||||
|
|
||||||
|
If you can't bear two versions of truth spoken
|
||||||
|
Produced by silos to make a trap for fools,
|
||||||
|
Or watch systems you gave your life to, broken,
|
||||||
|
And stoop and build 'em up with off-the-shelf tools;
|
||||||
|
|
||||||
|
|
||||||
|
If you would make one lake with all your data
|
||||||
|
And risk it on one vendor's big bang plan,
|
||||||
|
And lose, and start again at invitation-to-tender
|
||||||
|
And salvage from the project, what you can:
|
||||||
|
|
||||||
|
If you can force your flat files and mainframe
|
||||||
|
To serve datamarts when key DBAs retire,
|
||||||
|
And so hold on when stakeholders proclaim
|
||||||
|
Why (oh why) is our Data Quality so dire?
|
||||||
|
|
||||||
|
|
||||||
|
If you can talk with architects & keep your virtue,
|
||||||
|
Or walk with CEOs - nor lose the accounting touch,
|
||||||
|
If neither UTF-8 nor PII can hurt you,
|
||||||
|
If all teams count with you, but none too much:
|
||||||
|
|
||||||
|
If you can keep Data Owners beholden
|
||||||
|
To uploads that are timely and accurate and whole,
|
||||||
|
Yours is the Earth and clean records (golden),
|
||||||
|
And - which is more - you'll have Data Control!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*If you can't keep your data when all about you are losing theirs - [Data Controller for SAS](https://datacontroller.io) can help. Capture, review and approval for every change, with a full audit trail, on SAS Viya, SAS 9 EBI and SASjs Server.*
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Source LinkedIn post:
|
||||||
|
|
||||||
|
If you can keep your data when all about you
|
||||||
|
are losing theirs, and blaming it on you...
|
||||||
|
|
||||||
|
#datagovernance #dataquality #masterdatamanagement
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Image prompt:
|
||||||
|
Classic book title-page cover on a dark slate (#314351) background with a faint vignette and dot grid. A centred cream paper card with a drop shadow, double rule and green spine hint holds the title page: "FIRST PUBLISHED 2022" in small caps, green ornament rules, "IF" in large bold serif, "An Ode to Data Control" in serif, "after Rudyard Kipling" in italic, and the closing couplet "And - which is more - you'll have Data Control!" above a green rule. 1200x627 landscape, composed programmatically with PIL in the Data Controller brand palette rather than generated by an image model.
|
||||||
|
-->
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
title: '28 Ways to Be Missing in SAS'
|
||||||
|
description: A SAS numeric missing is not a lone wolf - there are 28 of them, and Data Controller has handled all of them since v4. How they work, and what the validation rules do with them.
|
||||||
|
date: '2026-09-22 09:00:00'
|
||||||
|
author: 'Allan Bowe'
|
||||||
|
authorLink: https://www.linkedin.com/in/allanbowe/
|
||||||
|
tags:
|
||||||
|
- Special Missings
|
||||||
|
- Data Quality
|
||||||
|
---
|
||||||
|
|
||||||
|
# 28 Ways to Be Missing in SAS
|
||||||
|
|
||||||
|
A numeric missing value in SAS is not a lone wolf. The ordinary missing (`.`) is one of **28** distinct missing values available for a numeric variable - the other 27 are written with a single character, the letters `A` to `Z` or an underscore (`._`).
|
||||||
|
|
||||||
|
They exist because "missing" is usually not the whole story. A survey question that was never reached, a reading that was illegible, a value the respondent refused to give - in a well run process those are different facts, and a lone `.` throws the difference away. Special missings record *why* the value is missing.
|
||||||
|
|
||||||
|
## They are numbers, not text
|
||||||
|
|
||||||
|
- Arithmetic on them yields missing - `.A + 1` is `.`
|
||||||
|
- `proc means`, `proc summary` and friends exclude them, exactly as they exclude `.`
|
||||||
|
- They sort below every non-missing number, in the order `._`, then `.`, then `.A` to `.Z`
|
||||||
|
- The `NMISS()` and `CMISS()` functions count them as missing
|
||||||
|
- Converting one to text drops the period - `cats(.A)` is the string `A`
|
||||||
|
|
||||||
|
In a SAS dataset they are written with a leading period (`.A`, `.B` ... `._`). In Data Controller you type just the letter or the underscore - no period - and the letter is not case sensitive. Two letters, or a letter mixed with a number, are refused rather than guessed.
|
||||||
|
|
||||||
|
## Carrying them between the browser and SAS
|
||||||
|
|
||||||
|
The Data Controller frontend and the SAS backend exchange data as JSON, and JSON has no way to express a letter as a numeric value - `A` is a string. The conversion is handled in the open source [SASjs Adapter](https://github.com/sasjs/adapter#variable-types):
|
||||||
|
|
||||||
|
- The adapter infers each column's SAS type from the values it is given. All numeric values means numeric; all strings means character; and a column holding a single character (`a`-`z`, `_` or `.`) alongside numeric values is numeric, with the lone characters written to SAS as special missings.
|
||||||
|
- `null` becomes `.` or an empty string, according to the type derived for that column.
|
||||||
|
- Two cases cannot be inferred from the values alone: a numeric column containing *only* special missings looks like a single character column, and a character column containing only nulls looks numeric. For those, the adapter accepts an explicit format for the column - and Data Controller sends one automatically, because it already knows each column's SAS format from the metadata returned by the backend.
|
||||||
|
- A value that is neither a number nor a single valid character is refused rather than guessed (for example `aaaa`, or `!` in a numeric column), and a literal `.` is refused in favour of `null` for a regular missing.
|
||||||
|
|
||||||
|
There is nothing to configure. Special missings are available by default, for numeric cells.
|
||||||
|
|
||||||
|
Once in SAS they are ordinary values, so they are what the approval DIFF screen compares, and the DIFF's formatted / unformatted switch shows either the formatted representation or the raw value - useful for confirming exactly which missing was set.
|
||||||
|
|
||||||
|
## What the validation rules do with them
|
||||||
|
|
||||||
|
A special missing is a value somebody deliberately set, so the rules treat it as a value - it is not blank, and it is not exempt:
|
||||||
|
|
||||||
|
- `NOTNULL` - passes, because it is not null
|
||||||
|
- `MINVAL` - fails, because a special missing sorts below every number and so is below any minimum
|
||||||
|
- `MAXVAL` - passes, for the same reason it is below any maximum
|
||||||
|
- `CASE` (`UPCASE` / `LOWCASE`) - compared as text, so `A` passes `UPCASE` but `a` does not
|
||||||
|
- `HARDREGEX` / `SOFTREGEX` - checked against the pattern like any other value; unlike blanks and the plain `.`, special missings are **not** exempt, so a numeric column that carries them needs a pattern which allows for a single letter
|
||||||
|
|
||||||
|
That is usually what you want - a value recorded as "not collected" should not quietly satisfy a completeness or range rule. It does mean a range rule on a column that uses special missings will warn on those rows, which is the signal to either accommodate them in the rule or not use special missings on that column.
|
||||||
|
|
||||||
|
`video: [Managing Special Missing Values with Data Controller for SAS](https://www.youtube-nocookie.com/embed/ggrcNr23Jzw)`
|
||||||
|
|
||||||
|
The recording shows the full cycle: typing special missings into numeric cells, the rejections, submitting the changes, approving them, and reviewing the DIFF - including a change from one special missing to another, and the formatted / unformatted switch on date and datetime columns.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
LinkedIn version of this post - publish it with the "Managing Special Missing
|
||||||
|
Values with Data Controller for SAS" video attached. Kept in sync with the copy
|
||||||
|
above: same points, same claims, same order.
|
||||||
|
|
||||||
|
A SAS numeric missing is not a lone wolf. It is 28.
|
||||||
|
|
||||||
|
The ordinary missing (.) is just the most common of them. The other 27 are single characters - the letters A to Z, or an underscore (._) - and they let you record WHY a value is missing: the question was never reached, the reading was illegible, the respondent refused.
|
||||||
|
|
||||||
|
They are real numeric values, not text:
|
||||||
|
|
||||||
|
- .A + 1 is .
|
||||||
|
- PROC MEANS excludes them, like any other missing
|
||||||
|
- they sort below every number: ._ then . then .A to .Z
|
||||||
|
- NMISS() and CMISS() count them as missing
|
||||||
|
|
||||||
|
The hard part is every tool that is not SAS. JSON has no way to say "this number is a letter" - A is just a string. So a value that is perfectly legal in a SAS dataset quietly breaks in the browser, in Excel, in an API.
|
||||||
|
|
||||||
|
We solved that in the open source SASjs Adapter, and it has been in Data Controller for SAS since v4:
|
||||||
|
|
||||||
|
- the adapter infers the column type from the values, so a column of numbers that also holds a lone letter is written to SAS as numeric, with the letter as a special missing
|
||||||
|
- where the type cannot be inferred - a numeric column holding ONLY special missings - Data Controller passes the column format explicitly
|
||||||
|
- you type the letter on its own: a or A, no period, and case does not matter
|
||||||
|
|
||||||
|
And they behave like values in DC's validation rules, which is usually what you want:
|
||||||
|
|
||||||
|
- NOTNULL passes - it is not null
|
||||||
|
- MINVAL fails - it sorts below every number; MAXVAL passes for the same reason
|
||||||
|
- HARDREGEX checks it against the pattern, unlike blanks and plain .
|
||||||
|
|
||||||
|
So a value recorded as "not collected" cannot quietly satisfy a completeness or range rule.
|
||||||
|
|
||||||
|
The video shows the whole cycle - entering them, the rejections, submit, approve, and the DIFF.
|
||||||
|
|
||||||
|
#sas #datacapture #mdm #dataquality
|
||||||
|
|
||||||
|
Image prompt: a pack of 28 wolves fanning out across a snowy plain at dusk, one wolf standing apart to the left, wide cinematic composition, cool blue-grey palette with a single warm amber accent on the lone wolf, soft depth of field, no text and no logos, 16:9, editorial cover art for a technical blog post about the 28 SAS missing values. Save the generated image as ./cover.jpeg and set previewImg: './cover.jpeg' in the front matter above.
|
||||||
|
-->
|
||||||
@@ -21,11 +21,6 @@ const Footer = () => (
|
|||||||
on <StyledAnchor href="https://sasapps.io">SAS Web Apps</StyledAnchor>
|
on <StyledAnchor href="https://sasapps.io">SAS Web Apps</StyledAnchor>
|
||||||
.
|
.
|
||||||
</StyledDesc>
|
</StyledDesc>
|
||||||
<StyledDesc>
|
|
||||||
<StyledAnchor href="https://sasapps.io/cyber-essentials-certified/">
|
|
||||||
Cyber Essentials Certified
|
|
||||||
</StyledAnchor>
|
|
||||||
</StyledDesc>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-3">
|
<div className="col-md-3">
|
||||||
<StyledHeading>Source Code</StyledHeading>
|
<StyledHeading>Source Code</StyledHeading>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import { FaShieldAlt } from 'react-icons/fa'
|
|
||||||
|
|
||||||
import { Hero, HeroHeading, HeroDesc, HeroBadge } from './style'
|
import { Hero, HeroHeading, HeroDesc } from './style'
|
||||||
import { BottomSectionArrow, OutlineButton } from '../shared/styledComponents'
|
import { BottomSectionArrow, OutlineButton } from '../shared/styledComponents'
|
||||||
import { Container } from '../shared'
|
import { Container } from '../shared'
|
||||||
|
|
||||||
@@ -20,18 +19,9 @@ const HeroSection: React.FC<HeroSectionProps> = ({
|
|||||||
<HeroHeading>{heading}</HeroHeading>
|
<HeroHeading>{heading}</HeroHeading>
|
||||||
<HeroDesc>{desc}</HeroDesc>
|
<HeroDesc>{desc}</HeroDesc>
|
||||||
{location.pathname === pathPrefix + '/' && (
|
{location.pathname === pathPrefix + '/' && (
|
||||||
<>
|
|
||||||
<HeroBadge
|
|
||||||
href="https://sasapps.io/cyber-essentials-certified/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<FaShieldAlt /> Cyber Essentials Certified
|
|
||||||
</HeroBadge>
|
|
||||||
<Link to="/contact/">
|
<Link to="/contact/">
|
||||||
<OutlineButton>Try Data Controller</OutlineButton>
|
<OutlineButton>Try Data Controller</OutlineButton>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
<BottomSectionArrow />
|
<BottomSectionArrow />
|
||||||
|
|||||||
@@ -17,20 +17,3 @@ export const HeroHeading = styled.h1`
|
|||||||
export const HeroDesc = styled.p`
|
export const HeroDesc = styled.p`
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const HeroBadge = styled.a`
|
|
||||||
display: block;
|
|
||||||
width: fit-content;
|
|
||||||
margin: 0.75rem 0 0;
|
|
||||||
padding: 0.375rem 1rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
&:hover {
|
|
||||||
color: white;
|
|
||||||
border-color: white;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { FaShieldAlt } from 'react-icons/fa'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const StyledCertBadge = styled.a`
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.375rem 1rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
border: 2px solid #314351;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
color: #314351;
|
||||||
|
text-decoration: none;
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: #314351;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const CertBadge = () => (
|
||||||
|
<StyledCertBadge
|
||||||
|
href="https://sasapps.io/cyber-essentials-certified/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<FaShieldAlt /> Cyber Essentials Certified
|
||||||
|
</StyledCertBadge>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default CertBadge
|
||||||
@@ -5,6 +5,7 @@ import Layout from '../components/layout'
|
|||||||
import Seo from '../components/seo'
|
import Seo from '../components/seo'
|
||||||
|
|
||||||
import { Section } from '../components/shared'
|
import { Section } from '../components/shared'
|
||||||
|
import CertBadge from '../components/shared/certBadge'
|
||||||
import {
|
import {
|
||||||
SectionHeading,
|
SectionHeading,
|
||||||
SectionDesc
|
SectionDesc
|
||||||
@@ -37,6 +38,7 @@ const About: React.FC<PageProps<unknown>> = ({ location }) => {
|
|||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</SectionDesc>
|
</SectionDesc>
|
||||||
|
<CertBadge />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Layout from '../components/layout'
|
|||||||
import Seo from '../components/seo'
|
import Seo from '../components/seo'
|
||||||
|
|
||||||
import { Section, ScheduleDemo } from '../components/shared'
|
import { Section, ScheduleDemo } from '../components/shared'
|
||||||
|
import CertBadge from '../components/shared/certBadge'
|
||||||
import {
|
import {
|
||||||
SectionHeading,
|
SectionHeading,
|
||||||
SectionDesc
|
SectionDesc
|
||||||
@@ -98,6 +99,7 @@ const Home: React.FC<PageProps<IndexPageData>> = ({ data, location }) => {
|
|||||||
perform manual data uploads into their preferred database, in
|
perform manual data uploads into their preferred database, in
|
||||||
real-time, with full validation, approval, security, and control.
|
real-time, with full validation, approval, security, and control.
|
||||||
</SectionDesc>
|
</SectionDesc>
|
||||||
|
<CertBadge />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-3">
|
<div className="col-md-3">
|
||||||
<Art src={rightArt} info="Clinical Research Data" />
|
<Art src={rightArt} info="Clinical Research Data" />
|
||||||
|
|||||||
@@ -118,6 +118,22 @@ const StyledContent = styled.div`
|
|||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
font-family: Monaco, 'Andale Mono', 'Courier New', Courier, monospace;
|
font-family: Monaco, 'Andale Mono', 'Courier New', Courier, monospace;
|
||||||
}
|
}
|
||||||
|
/* A fenced block tagged with the 'poem' language is verse, not code: drop
|
||||||
|
the code chrome and set it in the serif face, keeping the author's line
|
||||||
|
breaks and blank lines (which read as stanza breaks). */
|
||||||
|
pre:has(> code.language-poem) {
|
||||||
|
background-image: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 1.8rem 0;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
pre > code.language-poem {
|
||||||
|
font-family: Georgia, 'Times New Roman', serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
color: #222222;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
interface PostProps {
|
interface PostProps {
|
||||||
|
|||||||