chore(site): enable strict typescript and fix issues (#327)

This commit is contained in:
Rickard Natt och Dag 2021-04-23 15:52:13 +02:00 committed by GitHub
parent 8df0eb76f2
commit 70999a990e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 289 additions and 248 deletions

View File

@ -10,6 +10,8 @@
}
},
"rules": {
// No typing in project
"react/prop-types": "off",
// React included automatically in Next.js
"react/react-in-jsx-scope": "off",
// Allow unescaped characters like '

View File

@ -8,7 +8,7 @@ import screenshotLogin from '../assets/img/screenshots/screenshot_login.png'
import SectionTitle from './SectionTitle'
import Image from 'next/image'
const AppShots = () => {
const AppShots = (): JSX.Element => {
return (
<section
className="max-w-6xl px-5 py-8 mx-auto md:px-0 md:py-32"

View File

@ -15,7 +15,7 @@ import { H1 } from './Typography'
import { useIntl } from 'react-intl'
import { ButtonLinkPatreon } from './ButtonLink'
const Banner = () => {
const Banner = (): JSX.Element => {
const intl = useIntl()
return (

View File

@ -1,13 +1,8 @@
import Link from 'next/link'
import { ReactNode } from 'react'
interface ButtonLinkProps {
children: ReactNode
href: string
target?: string
}
type ButtonLinkProps = Pick<HTMLAnchorElement, 'href' | 'target'>
const ButtonLink = ({ children, href, target }: ButtonLinkProps) => {
const ButtonLink: React.FC<ButtonLinkProps> = ({ children, href, target }) => {
return (
<a
href={href}
@ -20,15 +15,12 @@ const ButtonLink = ({ children, href, target }: ButtonLinkProps) => {
)
}
interface ButtonLinkInternalProps {
children: ReactNode
href: string
}
type ButtonLinkInternalProps = Pick<HTMLAnchorElement, 'href'>
export const ButtonLinkInternal = ({
export const ButtonLinkInternal: React.FC<ButtonLinkInternalProps> = ({
children,
href,
}: ButtonLinkInternalProps) => {
}) => {
return (
<Link href={href}>
<a className="inline-block px-4 py-2 font-bold text-indigo-800 border-2 border-indigo-800 rounded-full cursor-pointer md:px-8 md:py-4 hover:bg-indigo-800 hover:text-white">
@ -38,15 +30,11 @@ export const ButtonLinkInternal = ({
)
}
interface ButtonLinkPatreonProps {
children: ReactNode
}
export const ButtonLinkPatreon = ({ children }: ButtonLinkPatreonProps) => {
export const ButtonLinkPatreon: React.FC = ({ children }) => {
return (
<a
href="https://www.patreon.com/oppnaskolplattformen"
className="inline-block py-2 px-4 md:px-8 md:py-4 font-bold text-white border-2 bg-red-500 rounded-full hover:bg-white hover:border-red-500 hover:text-red-500"
className="inline-block px-4 py-2 font-bold text-white bg-red-500 border-2 rounded-full md:px-8 md:py-4 hover:bg-white hover:border-red-500 hover:text-red-500"
target="_blank"
rel="noopener noreferrer"
>

View File

@ -4,7 +4,7 @@ import DownloadButtons from './DownloadButtons'
import Section from './Section'
import { H2 } from './Typography'
const CtaThree = () => {
const CtaThree = (): JSX.Element => {
return (
<Section bg="bg-white">
<div>

View File

@ -4,7 +4,7 @@ import DownloadButtons from './DownloadButtons'
import Section from './Section'
import { H2 } from './Typography'
const CtaTwo = () => {
const CtaTwo = (): JSX.Element => {
return (
<Section>
<Image src={img1} width="668" height="500" alt="" />

View File

@ -1,6 +1,6 @@
import ButtonLink, { ButtonLinkPatreon } from './ButtonLink'
const DownloadButtons = () => {
const DownloadButtons = (): JSX.Element => {
return (
<div className="space-x-4">
<ButtonLink
@ -16,9 +16,7 @@ const DownloadButtons = () => {
>
Play Store
</ButtonLink>
<ButtonLinkPatreon>
Stöd oss Patreon!
</ButtonLinkPatreon>
<ButtonLinkPatreon>Stöd oss Patreon!</ButtonLinkPatreon>
</div>
)
}

View File

@ -1,6 +1,18 @@
import classnames from 'classnames'
const FeatureCard = ({ image, title, text, isActive }) => {
interface FeatureCardProps {
image: string
title: string
text: string
isActive: boolean
}
const FeatureCard = ({
image,
title,
text,
isActive,
}: FeatureCardProps): JSX.Element => {
return (
<div
className={classnames(

View File

@ -41,7 +41,7 @@ const FEATURES_DATA = [
},
]
const Features = () => {
const Features = (): JSX.Element => {
const swiperParams: SwiperOptions = {
slidesPerView: 3,
slidesPerGroup: 3,
@ -83,7 +83,7 @@ const Features = () => {
}
return (
<section className="max-w-6xl mx-auto px-5 md:px-0" id="funktioner">
<section className="max-w-6xl px-5 mx-auto md:px-0" id="funktioner">
<div className="max-w-2xl mx-auto">
<SectionTitle
title="Enkelhet och snabbhet"
@ -91,14 +91,14 @@ const Features = () => {
/>
</div>
<Swiper className="feature-carousel" {...swiperParams}>
{FEATURES_DATA.map((feature) => (
<SwiperSlide key={feature.title}>
{({ isActive }) => (
{FEATURES_DATA.map(({ title, text, image }) => (
<SwiperSlide key={title}>
{({ isActive }: { isActive: boolean }) => (
<FeatureCard
isActive={isActive}
title={feature.title}
text={feature.text}
image={feature.image}
title={title}
text={text}
image={image}
/>
)}
</SwiperSlide>

View File

@ -10,7 +10,7 @@ const team = [
{ name: 'Öppna skolplattformen', twitter: 'oppnaskolplatt' },
]
const Footer = () => {
const Footer = (): JSX.Element => {
return (
<footer>
<div className="items-start max-w-6xl px-5 py-8 mx-auto lg:py-12 grid lg:px-4 grid-cols-1 lg:grid-cols-4 gap-x-12 gap-y-5">

View File

@ -21,7 +21,7 @@ const FUNFACTS_DATA = [
},
]
const FunFacts = () => {
const FunFacts = (): JSX.Element => {
const [counter, setCounter] = React.useState({
startCounter: false,
})
@ -37,7 +37,7 @@ const FunFacts = () => {
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-y-5">
{FUNFACTS_DATA.map((funfact) => (
<div className="text-center" key={funfact.title}>
<span className="text-4xl sm:text-5xl text-indigo-500 whitespace-nowrap">
<span className="text-4xl text-indigo-500 sm:text-5xl whitespace-nowrap">
<VisibilitySensor
onChange={onVisibilityChange}
offset={{ top: 10 }}

View File

@ -28,7 +28,7 @@ const useMobile = (cb: UseMobileCallback) => {
}, [])
}
const HeaderHome = () => {
const HeaderHome = (): JSX.Element => {
const [displayMobileMenu, setDisplayMobileMenu] = React.useState(false)
useMobile((e) => {

View File

@ -1,4 +1,4 @@
export const Check = () => {
export const Check = (): JSX.Element => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
@ -14,7 +14,7 @@ export const Check = () => {
)
}
export const Times = () => {
export const Times = (): JSX.Element => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
@ -30,7 +30,7 @@ export const Times = () => {
)
}
export const Twitter = () => {
export const Twitter = (): JSX.Element => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
@ -42,7 +42,7 @@ export const Twitter = () => {
)
}
export const Menu = () => {
export const Menu = (): JSX.Element => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"

View File

@ -1,15 +1,16 @@
import Head from 'next/head'
import { ReactNode } from 'react'
import favImg from '../assets/img/favicon.png'
import logo from '../assets/img/logo.png'
import { GA_TRACKING_ID } from './gtag'
interface LayoutProps {
children: ReactNode
pageTitle: string
}
const Layout = ({ children, pageTitle }: LayoutProps) => {
const Layout: React.FC<LayoutProps> = ({
children,
pageTitle,
}): JSX.Element => {
return (
<div>
<Head>

View File

@ -1,13 +1,14 @@
import Link from 'next/link'
import classnames from 'classnames'
import { ReactNode } from 'react'
import Link from 'next/link'
interface LinkInternalProps {
children: ReactNode
href: string
}
const Internal = ({ href, children }: LinkInternalProps): JSX.Element => {
const Internal: React.FC<LinkInternalProps> = ({
href,
children,
}): JSX.Element => {
return (
<Link href={href}>
<a className="text-indigo-800">{children}</a>
@ -16,18 +17,17 @@ const Internal = ({ href, children }: LinkInternalProps): JSX.Element => {
}
interface LinkExternalProps {
children: ReactNode
className?: string
href: string
target?: string
}
const External = ({
const External: React.FC<LinkExternalProps> = ({
className,
href,
children,
target = '_blank',
}: LinkExternalProps): JSX.Element => {
}): JSX.Element => {
return (
<a
className={classnames('text-indigo-800', className)}

View File

@ -8,7 +8,12 @@ interface NavLinksProps {
onClick?: () => void
}
const NavLinks = ({ onClick }: NavLinksProps) => {
interface LinkProps {
href: string
to: string
}
const NavLinks = ({ onClick }: NavLinksProps): JSX.Element => {
const { pathname } = useRouter()
const intl = useIntl()
@ -18,7 +23,7 @@ const NavLinks = ({ onClick }: NavLinksProps) => {
return href.substring(0, hashIndex)
}
const Link = ({ href, to, children }) =>
const Link: React.FC<LinkProps> = ({ href, to, children }) =>
path(href) === pathname ? (
<ScrollLink
activeClass="current"

View File

@ -32,7 +32,7 @@ const baseFeatures = [
},
]
const Pricing = () => {
const Pricing = (): JSX.Element => {
return (
<section className="px-5 py-8 md:px-0 md:py-32" id="vad-kostar-det">
<div className="max-w-2xl mx-auto">

View File

@ -28,7 +28,7 @@ const baseFeatures = [
},
]
const Pricing = () => {
const Pricing = (): JSX.Element => {
return (
<section className="px-5 py-8 md:px-0 md:py-32" id="vad-kostar-det">
<div className="max-w-2xl mx-auto">

View File

@ -1,4 +1,4 @@
const Privacy = () => {
const Privacy = (): JSX.Element => {
return (
<div className="header">
<div className="max-w-6xl mx-auto px-5 md:px-0 my-5 md:my-24 prose">

View File

@ -1,49 +1,52 @@
const QA = () => {
const QA = (): JSX.Element => {
return (
<div className="header">
<div className="max-w-6xl px-5 mx-auto my-5 md:my-24 md:px-0 prose">
<h1>Frågor och svar om Öppna skolplattformen</h1>
<h2>Om Utbildningsförvaltningens juridiska utredning och polisanmälan</h2>
<h2>
Om Utbildningsförvaltningens juridiska utredning och polisanmälan
</h2>
<p>
Den 15 april släppte Utbildningsförvaltningen sin juridiska utredning
av Öppna skolplattformen och den 16 april ska de ha polisanmält oss.
Vi är förvånade över att utbildningsdirektör Lena Holmdahl agerar ,
men tycker samtidigt att det ska bli skönt att någon annan än Utbildningsförvaltningen
granskar ärendet. Därmed kanske vi kan stopp några av de missförstånd
som nu sprids av förvaltningen, som att vi skulle ha bett om tillgång
till Skolplattformens API (vi bad om <u>dokumentation</u> av API:et,
vilket är en helt annan sak. Det vill de inte lämna ut och det beslutet
har vi överklagat till kammarrätten).
Nedan kommenterar vi delar av Utbildningsförvaltningens eget fråga-svar-dokument.
men tycker samtidigt att det ska bli skönt att någon annan än
Utbildningsförvaltningen granskar ärendet. Därmed kanske vi kan
stopp några av de missförstånd som nu sprids av förvaltningen, som
att vi skulle ha bett om tillgång till Skolplattformens API (vi bad om{' '}
<u>dokumentation</u> av API:et, vilket är en helt annan sak. Det vill
de inte lämna ut och det beslutet har vi överklagat till
kammarrätten). Nedan kommenterar vi delar av Utbildningsförvaltningens
eget fråga-svar-dokument.
</p>
<h3>Vad handlar Stockholms stads utredning om?</h3>
<h4>Utbildningsförvaltningens svar</h4>
<p>
Stockholms stad har gjort en utredning av juridiken kring Öppna skolplattformens
betal-app, som utan stadens medgivande behandlar information från
Stockholms stads skolplattform.
Stockholms stad har gjort en utredning av juridiken kring Öppna
skolplattformens betal-app, som utan stadens medgivande behandlar
information från Stockholms stads skolplattform.
</p>
<h4>Vår kommentar</h4>
<p>
Detta är ett felaktigt påstående. Vår app Öppna skolplattformen använder
ett av Stockholms stads tillgängliggjorda API:er. Appen kommer åt den
information som antingen är allmänt tillgänglig eller som användaren
efter stark autentisering (i detta fall BankID) själv begär från
Stockholms stads skolplattform.
Detta konstaterades redan i februari av de externa it-experter som
Utbildningsförvaltningen själva anlitat för att inspektera vår (öppet tillgängliga) källkod.
Detta är ett felaktigt påstående. Vår app Öppna skolplattformen
använder ett av Stockholms stads tillgängliggjorda API:er. Appen
kommer åt den information som antingen är allmänt tillgänglig eller
som användaren efter stark autentisering (i detta fall BankID) själv
begär från Stockholms stads skolplattform. Detta konstaterades redan i
februari av de externa it-experter som Utbildningsförvaltningen själva
anlitat för att inspektera vår (öppet tillgängliga) källkod.
</p>
<h2>Om appen Öppna skolplattformen</h2>
<h3>Vad är Öppna skolplattformen?</h3>
<p>
En app som ger föräldrar snabbare och lättare tillgång till sin egen
information Stockholms stads skolplattform. Appen är
som ett lager ovanpå den befintliga plattformen som gör att vi kan visa
samma information, men snabbare och med mindre krångel. Vi har byggt
den för föräldrar. Vi har inte byggt något som kan användas av lärare
eller elever, helt enkelt för att vi har utgått från den information
vi själva har tillgång till som föräldrar, och de behov vi har.
information Stockholms stads skolplattform. Appen är som ett lager
ovanpå den befintliga plattformen som gör att vi kan visa samma
information, men snabbare och med mindre krångel. Vi har byggt den för
föräldrar. Vi har inte byggt något som kan användas av lärare eller
elever, helt enkelt för att vi har utgått från den information vi
själva har tillgång till som föräldrar, och de behov vi har.
</p>
<h3>Vem ligger bakom Öppna skolplattformen?</h3>
<p>
@ -51,9 +54,10 @@ const QA = () => {
Christian Landgren och Johan Öbrink. Vi är utvecklare och föräldrar
till barn i grundskolan, och helt hänvisade till Skolplattformen i sin
nuvarande form. Vi har tillsammans mer än 75 års erfarenhet som
konsulter av att hantera personuppgifter (det gör vi dock inte i
Öppna skolplattformen! Vi ser aldrig er information, bara våra egna barns)
och är alla tre flera år i rad utnämnda till Sveriges bästa utvecklare av IDG.
konsulter av att hantera personuppgifter (det gör vi dock inte i Öppna
skolplattformen! Vi ser aldrig er information, bara våra egna barns)
och är alla tre flera år i rad utnämnda till Sveriges bästa utvecklare
av IDG.
</p>
<h3>Varför har ni skapat Öppna skolplattformen?</h3>
<p>
@ -61,27 +65,32 @@ const QA = () => {
informationen som finns i Skolplattformen men den är fruktansvärt
otillgänglig. Fråga vilken förälder eller lärare som helst. Vi är
utvecklare. Tekniken och kunskapen för att bygga ett bra verktyg
finns. Till slut gick det inte att tyst betrakta hur kommunen
bränner sådana oerhörda pengar en undermålig produkt, grund
av undermålig upphandling och okunnig kravställning.
finns. Till slut gick det inte att tyst betrakta hur kommunen bränner
sådana oerhörda pengar en undermålig produkt, grund av
undermålig upphandling och okunnig kravställning.
</p>
<h3>Vilken data har Öppna skolplattformen tillgång till?</h3>
<p>
Du som använder vår app har, när du identifierat dig gentemot Stockholms stad,
tillgång till samma data som genom kommunens app eller webbsida.
Vi bakom Öppna skolplattformen kan inte se någon annan information än våra egna barns.
Den information du hämtar visas bara för dig och hanteras bara din mobil eller surfplatta.
Ingen information skickas från den mobila enheten eller lagras,
analyseras eller processas någon annanstans. Inga tredjepartssystem
har tillgång till någon del av informationen.
Du som använder vår app har, när du identifierat dig gentemot
Stockholms stad, tillgång till samma data som genom kommunens app
eller webbsida. Vi bakom Öppna skolplattformen kan inte se någon annan
information än våra egna barns. Den information du hämtar visas bara
för dig och hanteras bara din mobil eller surfplatta. Ingen
information skickas från den mobila enheten eller lagras, analyseras
eller processas någon annanstans. Inga tredjepartssystem har tillgång
till någon del av informationen.
</p>
<h3>Är Öppna skolplattformen laglig?</h3>
<p>Ja.</p>
<h3>Hur säker är Öppna skolplattformen?</h3>
<p>
Din information är lika säker som i Skolplattformen. Vi är integritetsfanatiker och redogör
för vår syn dataskydd i Öppna skolplattformen i
<a href="https://skolplattformen.org/integritet">vår integritetspolicy</a>.
Din information är lika säker som i Skolplattformen. Vi är
integritetsfanatiker och redogör för vår syn dataskydd i Öppna
skolplattformen i
<a href="https://skolplattformen.org/integritet">
vår integritetspolicy
</a>
.
</p>
<h3>Men ni hanterar ju personinformation?</h3>
<p>
@ -90,27 +99,29 @@ const QA = () => {
Varje förälder legitimerar sig med BankID.
</p>
<h3>
Den kommunala Skolplattformen har kritiserats
för att hantera t ex personinformation ett osäkert sätt. Är detta
något ni sett exempel när ni gjort er utveckling? Några andra
brister i den kommunala Skolplattformen ni upptäckt?
Den kommunala Skolplattformen har kritiserats för att hantera t ex
personinformation ett osäkert sätt. Är detta något ni sett exempel
när ni gjort er utveckling? Några andra brister i den kommunala
Skolplattformen ni upptäckt?
</h3>
<p>
Ja, vi har upptäckt sårbarheter som inte borde finnas i ett sådant här system.
De har vi rapporterat ett ansvarsfullt sätt. Två fall har uppmärksammats
i media. I ett av fallen har kommunen täppt till luckan.
Vi kommer att fortsätta att rapportera eventuella sårbarheter vi upptäcker i vårt utvecklingsarbete.
Ja, vi har upptäckt sårbarheter som inte borde finnas i ett sådant här
system. De har vi rapporterat ett ansvarsfullt sätt. Två fall har
uppmärksammats i media. I ett av fallen har kommunen täppt till
luckan. Vi kommer att fortsätta att rapportera eventuella sårbarheter
vi upptäcker i vårt utvecklingsarbete.
</p>
<h3>
Vilken information och funktionalitet finns idag tillgänglig i Öppna
skolplattformen?
</h3>
<p>
Vi har gjort det lättare att läsa veckobrev och matsedel, hitta notifieringar och frånvaroanmäla.
Vi gör nya releaser hela tiden och har byggt appen som man bygger appar i
dag, inte som man gjorde för sju år sedan. Vi har utgått från föräldrars önskemål
inte minst att de som är förälder till flera barn lättare
ska kunna fullständig överblick av kommande prov, läxor och idrottsdagar.
Vi har gjort det lättare att läsa veckobrev och matsedel, hitta
notifieringar och frånvaroanmäla. Vi gör nya releaser hela tiden och
har byggt appen som man bygger appar i dag, inte som man gjorde för
sju år sedan. Vi har utgått från föräldrars önskemål inte minst
att de som är förälder till flera barn lättare ska kunna
fullständig överblick av kommande prov, läxor och idrottsdagar.
</p>
<h3>
Vilken information/funktionalitet kommer att finnas tillgänglig?
@ -120,11 +131,10 @@ const QA = () => {
veckobrev att aktiviteter placeras ut en kalender dvs att en
förälder till tre barn ska en vy av vad som ska packas med till
skolan under nästa vecka en och samma plats. Vi vill in flera
språkalternativ för att öka tillgängligheten.
Vi har massor av idéer till förbättringar som kommer dyka upp med
tiden och vi jobbar med löpande releaser snarare än att vänta
att allt är klart. Med er hjälp kommer vi kunna prioritera de funktioner
som hjälper mest.
språkalternativ för att öka tillgängligheten. Vi har massor av idéer
till förbättringar som kommer dyka upp med tiden och vi jobbar med
löpande releaser snarare än att vänta att allt är klart. Med er
hjälp kommer vi kunna prioritera de funktioner som hjälper mest.
</p>
<h3>
Jag är lärare eller annan typ av användare. Snälla bygg något åt mig!
@ -147,12 +157,12 @@ const QA = () => {
Förutom att hjälpa oss själva slippa Skolplattformens horribla
användarupplevelse har vi försökt illustrera en alternativ väg framåt.
Vi byggde litet och utgick helt och hållet från användarbehov (dvs
vårt eget). All kod är släppt som öppen källkod att den kan granskas
av andra och ett större värde kan komma världen till godo. borde
den offentliga sektorn alltid arbeta. Snarare än samordna, planera och
krava: göra, utforska och dela med sig. Snarare än , stora, rigida
projekt: många, små, agila. Snarare än gårdagens misslyckade
IT-satsningar: öppna API:er, öppen källkod och öppen data.
vårt eget). All kod är släppt som öppen källkod att den kan
granskas av andra och ett större värde kan komma världen till godo.
borde den offentliga sektorn alltid arbeta. Snarare än samordna,
planera och krava: göra, utforska och dela med sig. Snarare än ,
stora, rigida projekt: många, små, agila. Snarare än gårdagens
misslyckade IT-satsningar: öppna API:er, öppen källkod och öppen data.
</p>
<h3>Vad är er syn den kommunala Skolplattformen?</h3>
<p>
@ -166,11 +176,11 @@ const QA = () => {
</p>
<h3>Vad anser ni borde göras med Skolplattformen?</h3>
<p>
Skolplattformen är uppbyggd i flera delar. Dels
innehåller den bakomliggande delar som hanterar informationen som
lagras ett säkert sätt och dels innehåller den en webbsida och app
som hämtar informationen och presenterar den. Vad vi förstår har mest
energi lagts de bakomliggande delarna. Resultatet av det är att
Skolplattformen är uppbyggd i flera delar. Dels innehåller den
bakomliggande delar som hanterar informationen som lagras ett
säkert sätt och dels innehåller den en webbsida och app som hämtar
informationen och presenterar den. Vad vi förstår har mest energi
lagts de bakomliggande delarna. Resultatet av det är att
användarupplevelsen för många målgrupper har blivit lidande. Vi har
visat att det med relativt små resurser går att börja om från början
och bygga en ny s.k. frontend dvs app och webbsida som återanvänder
@ -198,31 +208,34 @@ const QA = () => {
om det ni gjort?
</h3>
<p>
Framför allt har vi anmält ett antal säkerhetsluckor i Stockholms stads it-system.
En av dem har anmälts till IMY, den andra ännu inte.
Eftersom den är allvarlig och vi inte är trygga med att Utbildningsförvaltningen
utrett om information läckt ett bra sätt kommer vi att insistera att den anmäls.
Utbildningsförvaltningen har också anmält vår app.
Självklart kommer vi att redovisa IMY:s ställningstagande när det kommer.
Framför allt har vi anmält ett antal säkerhetsluckor i Stockholms
stads it-system. En av dem har anmälts till IMY, den andra ännu inte.
Eftersom den är allvarlig och vi inte är trygga med att
Utbildningsförvaltningen utrett om information läckt ett bra sätt
kommer vi att insistera att den anmäls. Utbildningsförvaltningen
har också anmält vår app. Självklart kommer vi att redovisa IMY:s
ställningstagande när det kommer.
</p>
<h3>Hur har kommunen reagerat?</h3>
<p>
Inte som vi hade hoppats, trots flera möten där vi förklarat exakt vad
appen gör rent tekniskt. Sammanfattningsvis kan man säga att de lagt mer
energi att misstänkliggöra oss än att täppa igen de stora säkerhetsluckor
vi hittat i deras system. Det är minst sagt olyckligt.
Du hittar alla våra svar deras utredning i ett separat QA-segment ovan.
appen gör rent tekniskt. Sammanfattningsvis kan man säga att de lagt
mer energi att misstänkliggöra oss än att täppa igen de stora
säkerhetsluckor vi hittat i deras system. Det är minst sagt olyckligt.
Du hittar alla våra svar deras utredning i ett separat QA-segment
ovan.
</p>
<h3>Varför reagerar de , tror ni?</h3>
<p>
Det är svårt att spekulera i. Men deras tekniska förståelse är förbluffande låg.
Det är svårt att spekulera i. Men deras tekniska förståelse är
förbluffande låg.
</p>
<h3>Är ni oroliga för att bli stämda?</h3>
<p>
Utbildningsdirektör Lena Holmdahl har polisanmält oss.
Det är förstås obehagligt och vi välkomnar inte slöseri med
polisens utredningsresurser. Men samtidigt är det skönt att ärendet
granskas av någon annan än Utbildningsförvaltningen.
Utbildningsdirektör Lena Holmdahl har polisanmält oss. Det är förstås
obehagligt och vi välkomnar inte slöseri med polisens
utredningsresurser. Men samtidigt är det skönt att ärendet granskas av
någon annan än Utbildningsförvaltningen.
</p>
<h3>Har ni rätt att använda varumärket Skolplattformen.</h3>
<p>
@ -235,15 +248,16 @@ const QA = () => {
</h3>
<p>
Appen kostar 12 kronor. Intäkten registreras i handelsbolaget Not Free
Beer som ägs av tre av utvecklarna och går till att täcka kostnader för inköp.
Det täcker inte långa vägar den tid vi lagt ner.
Med en låg engångskostnad ökar vi chansen att vi orkar syssla med
underhåll och uppdateringar. Vi vill ju ha en stabil lösning som
håller. Just nu jobbar vi egen fritid med något som förbättrar det
kommunen lagt en miljard av allmänna medel .
Beer som ägs av tre av utvecklarna och går till att täcka kostnader
för inköp. Det täcker inte långa vägar den tid vi lagt ner. Med en
låg engångskostnad ökar vi chansen att vi orkar syssla med underhåll
och uppdateringar. Vi vill ju ha en stabil lösning som håller. Just nu
jobbar vi egen fritid med något som förbättrar det kommunen lagt en
miljard av allmänna medel .
</p>
<h3>
Är det moraliskt att tjäna pengar något som kommunen borde erbjuda gratis?
Är det moraliskt att tjäna pengar något som kommunen borde erbjuda
gratis?
</h3>
<p>
Det är bra att föräldrar har en helt frivillig möjlighet att snabbare
@ -252,21 +266,28 @@ const QA = () => {
fungerar dåligt, både för oss föräldrar och för alla de lärare som
är helt utlämnade åt den.
</p>
<h2>Om hur appen Öppna skolplattformen utvecklats och hur du kan bidra till utvecklingen</h2>
<h2>
Om hur appen Öppna skolplattformen utvecklats och hur du kan bidra
till utvecklingen
</h2>
<h3>Hur rapporterar man buggar och önskemål?</h3>
<p>
Vi är enormt tacksamma för alla buggrapporter och förslag vi får och satsar mycket att
snabbt som möjligt fixa de saker som dyker upp. För att lite
ordning försöker vi samla alla buggar och önskemål samma ställe, Github.
Vi är enormt tacksamma för alla buggrapporter och förslag vi får och
satsar mycket att snabbt som möjligt fixa de saker som dyker
upp. För att lite ordning försöker vi samla alla buggar och
önskemål samma ställe, Github.
<a href="https://github.com/kolplattformen/skolplattformen/issues">
Klicka här</a> för att se vilka funktioner och buggar vi redan har tagit emot och jobbar .
Klicka här
</a>{' '}
för att se vilka funktioner och buggar vi redan har tagit emot och
jobbar .
</p>
<h3>Hur gick ni tillväga?</h3>
<p>
Allt bygger reverse engineering. Vi öppnade DevTools i Chrome och
loggade in. Sen antecknade vi alla url:er och payloads. Vi
tog koden för att anropa API:et och byggde ett npm-paket av den att
appen kunde köra proxyn i en process telefonen.
loggade in. Sen antecknade vi alla url:er och payloads. Vi tog koden
för att anropa API:et och byggde ett npm-paket av den att appen
kunde köra proxyn i en process telefonen.
</p>
<h3>Vilka tekniska utmaningar har ni överkommit och hur?</h3>
<p>
@ -286,8 +307,8 @@ const QA = () => {
Det är en anspelning hur GNU-projektet beskriver fri programvara:
To understand the concept, you should think of free as in free
speech, not as in free beer. vi har valt att tillgängliggöra all
kod som öppen källkod (Apache 2.0) men ändå ta betalt för appen, tyckte
vi att namnet var passande.
kod som öppen källkod (Apache 2.0) men ändå ta betalt för appen,
tyckte vi att namnet var passande.
</p>
<h3>Kontakta oss</h3>
<p>
@ -295,7 +316,7 @@ const QA = () => {
<a href="mailto:info@skolplattformen.org">dev@skolplattformen.org</a>.
</p>
</div>
</div >
</div>
)
}

View File

@ -1,19 +1,17 @@
import classnames from 'classnames'
import { ReactNode } from 'react'
interface SectionProps {
bg?: string
children: ReactNode
id?: string
padding?: string
}
const Section = ({
const Section: React.FC<SectionProps> = ({
bg = 'bg-gray-100',
padding = 'py-8 md:py-20',
children,
id,
}: SectionProps) => {
}) => {
return (
<section className={classnames('grid grid-main', bg, padding)} id={id}>
<div className="items-center grid gap-y-5 md:gap-y-20 col-start-3 col-end-4 grid-cols-1 lg:grid-cols-2">

View File

@ -3,7 +3,7 @@ interface SectionTitleProps {
title: string
}
const SectionTitle = ({ text, title }: SectionTitleProps) => {
const SectionTitle = ({ text, title }: SectionTitleProps): JSX.Element => {
return (
<div className="mb-16 text-center space-y-5">
<h2 className="text-4xl md:text-5xl font-bold leading-tight text-gray-800">

View File

@ -1,30 +1,39 @@
const Status = () => {
return (
<div className="max-w-6xl px-5 mx-auto my-5 md:my-24 md:px-0 prose">
<h1>Status</h1>
<h3>Funkar appen som den ska?</h3>
<p>
Senast vi kollade!
<br />
iPhone
<br />
Android
</p>
<p>
Vi har inga rapporter om att appen har problem för tillfället.
Har du upptäckt problem nu? Hjälp oss fixa det!
</p>
<p>
Det finns det tre sätt att göra det (i stigande ordning av braighet):
<ul>
<li>Skicka en tweet 🥉</li>
<li><a href="https://github.com/kolplattformen/skolplattformen/issues">Lägg en buggrapport här</a> 🥈</li>
<li><a href="https://github.com/kolplattformen/skolplattformen/pulls">Skicka en PR</a> 🥇</li>
</ul>
</p>
</div>
)
}
export default Status
const Status = (): JSX.Element => {
return (
<div className="max-w-6xl px-5 mx-auto my-5 md:my-24 md:px-0 prose">
<h1>Status</h1>
<h3>Funkar appen som den ska?</h3>
<p>
Senast vi kollade!
<br />
iPhone
<br /> Android
</p>
<p>
Vi har inga rapporter om att appen har problem för tillfället. Har du
upptäckt problem nu? Hjälp oss fixa det!
</p>
<p>
Det finns det tre sätt att göra det (i stigande ordning av braighet):
<ul>
<li>Skicka en tweet 🥉</li>
<li>
<a href="https://github.com/kolplattformen/skolplattformen/issues">
Lägg en buggrapport här
</a>{' '}
🥈
</li>
<li>
<a href="https://github.com/kolplattformen/skolplattformen/pulls">
Skicka en PR
</a>{' '}
🥇
</li>
</ul>
</p>
</div>
)
}
export default Status

View File

@ -37,7 +37,7 @@ export const testimonials = [
},
]
const Testimonials = () => {
const Testimonials = (): JSX.Element => {
return (
<Section padding="py-8 md:pt-32 md:pb-20">
{testimonials.map((testimonial) => (

View File

@ -16,7 +16,7 @@ interface TimelineProps {
events: TimelineEvent[]
}
const Timeline = ({ events }: TimelineProps) => {
const Timeline = ({ events }: TimelineProps): JSX.Element => {
return (
<ul className="max-w-2xl border-gray-200 md:mx-auto md:border-l-2 space-y-4 md:space-y-12">
{events.map(({ date, media, importantDates, overview }) => (
@ -64,7 +64,7 @@ const Timeline = ({ events }: TimelineProps) => {
</ul>
</div>
)}
{media?.length > 0 && (
{media.length > 0 && (
<div className="mt-4 text-sm">
<div className="font-bold">Media</div>
<ul className="pl-5 mt-2 list-disc space-y-2">

View File

@ -2,7 +2,7 @@ import { events } from '../data/timelineEvents'
import { ButtonLinkInternal } from './ButtonLink'
import Timeline from './Timeline'
const TimelineLatest = () => {
const TimelineLatest = (): JSX.Element => {
const latestMonthsEvents = events.slice(0, 1)
return (
@ -22,12 +22,14 @@ const TimelineLatest = () => {
uppmärksamhet.
</p>
<p>
Stockholms stads skolplattform har fått omfattande kritik för att
den kostat enorma pengar, inte är användarvänlig och har stora
säkerhetsbrister. Vår förväntan var att vårt initiativ skulle välkomnas och att vi skulle kunna samarbeta
med staden för att skapa ännu bättre lösningar. Istället
attackerar staden genom Utbildningsdirektör Lena Holmdahl appen och oss som privatpersoner, senast genom en polisanmälan.
I ett försök att skapa transparens har vi sammanställt vad som hänt nedan.
Stockholms stads skolplattform har fått omfattande kritik för att den
kostat enorma pengar, inte är användarvänlig och har stora
säkerhetsbrister. Vår förväntan var att vårt initiativ skulle
välkomnas och att vi skulle kunna samarbeta med staden för att skapa
ännu bättre lösningar. Istället attackerar staden genom
Utbildningsdirektör Lena Holmdahl appen och oss som privatpersoner,
senast genom en polisanmälan. I ett försök att skapa transparens har
vi sammanställt vad som hänt nedan.
</p>
<ButtonLinkInternal href="/aktuellt">
Läs hela historien

View File

@ -1,10 +1,4 @@
import { ReactNode } from 'react'
interface H1Props {
children: ReactNode
}
export const H1 = ({ children }: H1Props) => {
export const H1: React.FC = ({ children }) => {
return (
<h1 className="mb-5 text-4xl md:text-5xl font-semibold text-gray-800">
{children}
@ -12,11 +6,7 @@ export const H1 = ({ children }: H1Props) => {
)
}
interface H2Props {
children: ReactNode
}
export const H2 = ({ children }: H2Props) => {
export const H2: React.FC = ({ children }) => {
return (
<h2 className="mb-5 text-5xl font-semibold text-gray-800">{children}</h2>
)

View File

@ -27,7 +27,7 @@ test('pages renders correctly and displays important dates and media', () => {
test('handles missing Intl', () => {
const intl = global.Intl
global.Intl = undefined
global.Intl = (undefined as unknown) as typeof Intl
setup()

View File

@ -1,14 +1,26 @@
export const GA_TRACKING_ID = 'G-KX6E6T6FXS'
// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
export const pageview = (url) => {
export const pageview = (url: string): void => {
window.gtag('config', GA_TRACKING_ID, {
page_path: url.replace('#', ''),
})
}
interface GoogleEvent {
action: string
category: string
label: string
value: string
}
// https://developers.google.com/analytics/devguides/collection/gtagjs/events
export const event = ({ action, category, label, value }) => {
export const event = ({
action,
category,
label,
value,
}: GoogleEvent): void => {
window.gtag('event', action, {
value,
event_category: category,

View File

@ -18,7 +18,6 @@ const en = {
through Patreon? We'll see... Until April 29:th, the app is free
on both iOS and Android. After that we have to decide if it was a
good idea or not. So tip yor friends! And please support us on Patreon `,
}
} as const
export default en

View File

@ -1,5 +1,8 @@
import sv from './sv'
import en from './en'
export default { en, sv }
type LanguageKeys = keyof typeof en
export type Languages = 'sv' | 'en'
type Language = Record<LanguageKeys, string>
export default { en, sv } as Record<Languages, Language>

View File

@ -19,7 +19,6 @@ const sv = {
via Patreon? Vi får väl se... Fram till den 29 april är appen gratis
för både iOS och Android. Därefter måste vi bestämma oss för om det
var en bra idé. tipsa alla dina vänner! Och stöd oss gärna Patreon `,
}
} as const
export default sv

View File

@ -20,8 +20,8 @@ interface Event {
export interface TimelineEvent {
overview: ReactNode
date: string
importantDates?: Event[]
media?: Event[]
importantDates: Event[]
media: Event[]
}
export const events: TimelineEvent[] = [
@ -488,6 +488,7 @@ export const events: TimelineEvent[] = [
'Avslag på begäran om dokumentation med hänvisning till sekretess men utan lagrum',
},
],
media: [],
},
{
overview: (
@ -505,5 +506,6 @@ export const events: TimelineEvent[] = [
'Begäran om dokumentation av API (skickat till Kontaktcenter Stockholm)',
},
],
media: [],
},
]

View File

@ -8,19 +8,19 @@ import { useRouter } from 'next/router'
import { useEffect } from 'react'
import { IntlProvider } from 'react-intl'
import { pageview } from '../components/gtag'
import messages from '../content/locale/'
import messages, { Languages } from '../content/locale/'
import { AppProps } from 'next/app'
export default function App({ Component, pageProps }) {
export default function App({ Component, pageProps }: AppProps): JSX.Element {
const router = useRouter()
const { locale, defaultLocale } = router
const { locale = 'sv', defaultLocale } = router
const currentMessages = messages[locale]
const currentMessages = messages[locale as Languages]
// Google analytics
useEffect(() => {
const handleRouteChange = (url) => {
pageview(url)
}
const handleRouteChange = (url: string) => pageview(url)
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)

View File

@ -2,7 +2,7 @@ import Timeline from '../components/Timeline'
import { H1 } from '../components/Typography'
import { events } from '../data/timelineEvents'
const CurrentEventsPage = () => {
const CurrentEventsPage = (): JSX.Element => {
return (
<section className="mx-5 max-w-2xl md:mx-auto">
<div className="my-8 md:my-20">

View File

@ -8,7 +8,7 @@ import PricingTemp from '../components/PricingTemp'
import Testimonials from '../components/Testimonials'
import TimelineLatest from '../components/TimelineLatest'
const HomePage = () => {
const HomePage = (): JSX.Element => {
return (
<>
<Banner />

View File

@ -1,6 +1,6 @@
import Privacy from '../components/Privacy'
const IntegrityPage = () => {
const IntegrityPage = (): JSX.Element => {
return <Privacy />
}

View File

@ -1,6 +1,6 @@
import QA from '../components/QA'
const QAPage = () => {
const QAPage = (): JSX.Element => {
return <QA />
}

View File

@ -1,6 +1,6 @@
import Status from '../components/Status'
const StatusPage = () => {
const StatusPage = (): JSX.Element => {
return <Status />
}

View File

@ -8,7 +8,7 @@
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,

View File

@ -1,14 +1,14 @@
// test-utils.js
import React from 'react'
import { render as rtlRender } from '@testing-library/react'
import React, { ReactNode } from 'react'
import { render as rtlRender, RenderResult } from '@testing-library/react'
import { IntlProvider } from 'react-intl'
import messages from '../content/locale'
import messages, { Languages } from '../content/locale'
function render(
ui: React.ReactElement,
{ locale = 'sv', ...renderOptions } = {}
) {
function Wrapper({ children }) {
{ locale = 'sv' as Languages, ...renderOptions } = {}
): RenderResult {
function Wrapper({ children }: { children?: ReactNode }) {
return (
<IntlProvider locale={locale} messages={messages[locale]}>
{children}