docs-ui: replace CSS sync script with postcss-import
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -3,16 +3,12 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prebuild": "yarn sync:css",
|
||||
"build": "next build",
|
||||
"lint": "eslint .",
|
||||
"prestart": "yarn sync:css",
|
||||
"start": "next dev",
|
||||
"sync:changelog": "node scripts/sync-changelog.mjs",
|
||||
"sync:changelog:dry-run": "node scripts/sync-changelog.mjs --dry-run",
|
||||
"sync:changelog:force": "node scripts/sync-changelog.mjs --force",
|
||||
"sync:css": "node scripts/sync-css.js",
|
||||
"sync:css:watch": "node scripts/sync-css.js --watch"
|
||||
"sync:changelog:force": "node scripts/sync-changelog.mjs --force"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "19.2.10",
|
||||
@@ -46,10 +42,10 @@
|
||||
"@types/node": "^22.13.14",
|
||||
"@types/react": "19.2.10",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"chokidar": "^3.6.0",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.1.6",
|
||||
"lightningcss": "^1.28.2",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-import": "^16.1.1",
|
||||
"typescript": "^5",
|
||||
"unified": "^11.0.4"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
plugins: {
|
||||
'postcss-import': {},
|
||||
},
|
||||
};
|
||||
@@ -1,149 +0,0 @@
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { bundle } = require('lightningcss');
|
||||
const chokidar = require('chokidar');
|
||||
|
||||
// Configuration
|
||||
const config = {
|
||||
UIPath: '../../packages/ui',
|
||||
outputPath: '../src/css',
|
||||
files: [
|
||||
{
|
||||
source: 'css/styles.css',
|
||||
destination: 'theme-backstage.css',
|
||||
name: 'Main Styles',
|
||||
},
|
||||
{
|
||||
source: '../../.storybook/themes/spotify.css',
|
||||
destination: 'theme-spotify.css',
|
||||
name: 'Spotify Theme',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
class CSSSync {
|
||||
constructor() {
|
||||
this.UIPath = path.resolve(__dirname, config.UIPath);
|
||||
this.outputPath = path.resolve(__dirname, config.outputPath);
|
||||
this.isWatching = process.argv.includes('--watch');
|
||||
}
|
||||
|
||||
async syncFile(fileConfig) {
|
||||
const sourcePath = path.join(this.UIPath, fileConfig.source);
|
||||
const destPath = path.join(this.outputPath, fileConfig.destination);
|
||||
|
||||
try {
|
||||
// Check if source file exists
|
||||
if (!fs.existsSync(sourcePath)) {
|
||||
console.warn(`⚠️ Source file not found: ${sourcePath}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure destination directory exists
|
||||
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||
|
||||
// Bundle and optimize CSS
|
||||
const result = await bundle({
|
||||
filename: sourcePath,
|
||||
minify: true,
|
||||
});
|
||||
|
||||
// Write to destination
|
||||
fs.writeFileSync(destPath, result.code);
|
||||
|
||||
console.log(
|
||||
`✅ ${fileConfig.name}: ${fileConfig.source} → ${fileConfig.destination}`,
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`❌ Error syncing ${fileConfig.name}:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async syncAll() {
|
||||
console.log('🔄 Syncing CSS files...\n');
|
||||
|
||||
let successCount = 0;
|
||||
for (const fileConfig of config.files) {
|
||||
if (await this.syncFile(fileConfig)) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`\n✨ Synced ${successCount}/${config.files.length} CSS files successfully!`,
|
||||
);
|
||||
|
||||
if (successCount > 0) {
|
||||
console.log('\n📁 Available CSS files in src/css/:');
|
||||
config.files.forEach(file => {
|
||||
const destPath = path.join(this.outputPath, file.destination);
|
||||
if (fs.existsSync(destPath)) {
|
||||
const stats = fs.statSync(destPath);
|
||||
const size = (stats.size / 1024).toFixed(2);
|
||||
console.log(` • ${file.destination} (${size} KB)`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
startWatching() {
|
||||
console.log('👀 Watching for CSS changes...\n');
|
||||
|
||||
// Watch all source files
|
||||
const watchPaths = config.files.map(file =>
|
||||
path.join(this.UIPath, file.source),
|
||||
);
|
||||
|
||||
const watcher = chokidar.watch(watchPaths, {
|
||||
ignored: /node_modules/,
|
||||
persistent: true,
|
||||
});
|
||||
|
||||
watcher.on('change', async filePath => {
|
||||
console.log(
|
||||
`\n🔄 Change detected: ${path.relative(this.UIPath, filePath)}`,
|
||||
);
|
||||
|
||||
// Find which file changed and sync it
|
||||
const fileConfig = config.files.find(file =>
|
||||
filePath.endsWith(file.source.replace(/\//g, path.sep)),
|
||||
);
|
||||
|
||||
if (fileConfig) {
|
||||
await this.syncFile(fileConfig);
|
||||
}
|
||||
});
|
||||
|
||||
watcher.on('error', error => console.error('❌ Watch error:', error));
|
||||
|
||||
// Handle process termination
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n👋 Stopping CSS sync...');
|
||||
watcher.close();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
async run() {
|
||||
console.log('🎨 BUI CSS Sync Tool\n');
|
||||
console.log(`📂 BUI path: ${this.UIPath}`);
|
||||
console.log(`📂 Output path: ${this.outputPath}\n`);
|
||||
|
||||
// Initial sync
|
||||
await this.syncAll();
|
||||
|
||||
// Watch for changes if requested
|
||||
if (this.isWatching) {
|
||||
this.startWatching();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the sync tool
|
||||
const cssSync = new CSSSync();
|
||||
cssSync.run().catch(error => {
|
||||
console.error('❌ CSS Sync failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -49,10 +49,6 @@ export default async function RootLayout({
|
||||
data-theme-name="backstage"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/theme-backstage.css" />
|
||||
<link rel="stylesheet" href="/theme-spotify.css" />
|
||||
</head>
|
||||
<body>
|
||||
<Providers>
|
||||
<Sidebar />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,233 +1 @@
|
||||
@font-face {
|
||||
font-family: CircularSpTitle;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
|
||||
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
|
||||
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
|
||||
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
|
||||
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
|
||||
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
|
||||
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
|
||||
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
|
||||
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
|
||||
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
|
||||
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
|
||||
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
|
||||
U+FF0E, U+FF1A-FF1B, U+FFFF;
|
||||
src: url(https://encore.scdn.co/fonts/CircularSpTitle-Black-4588c99025b967475c31695b0743dd1a.woff2)
|
||||
format('woff2'),
|
||||
url(https://encore.scdn.co/fonts/CircularSpTitle-Black-506746f387a26f25aa3d023b3e501d34.woff)
|
||||
format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: CircularSpTitle;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
|
||||
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
|
||||
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
|
||||
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
|
||||
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
|
||||
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
|
||||
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
|
||||
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
|
||||
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
|
||||
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
|
||||
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
|
||||
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
|
||||
U+FF0E, U+FF1A-FF1B, U+FFFF;
|
||||
src: url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-b2586b06a2e1522e9d879d84c2792a58.woff2)
|
||||
format('woff2'),
|
||||
url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-1624fb2df28c20d7203d7fb86ce8b481.woff)
|
||||
format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: CircularSp;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
|
||||
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
|
||||
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
|
||||
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
|
||||
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
|
||||
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
|
||||
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
|
||||
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
|
||||
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
|
||||
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
|
||||
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
|
||||
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
|
||||
U+FF0E, U+FF1A-FF1B, U+FFFF;
|
||||
src: url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)
|
||||
format('woff2'),
|
||||
url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)
|
||||
format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: CircularSp;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
|
||||
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
|
||||
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
|
||||
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
|
||||
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
|
||||
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
|
||||
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
|
||||
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
|
||||
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
|
||||
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
|
||||
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
|
||||
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
|
||||
U+FF0E, U+FF1A-FF1B, U+FFFF;
|
||||
src: url(https://encore.scdn.co/fonts/CircularSp-Book-a00e99ef9996a3a157fb6b746856d04f.woff2)
|
||||
format('woff2'),
|
||||
url(https://encore.scdn.co/fonts/CircularSp-Book-3f73da7d35bd81c706bce7bbb84964de.woff)
|
||||
format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: CircularSp;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
|
||||
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
|
||||
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
|
||||
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
|
||||
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
|
||||
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
|
||||
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
|
||||
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
|
||||
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
|
||||
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
|
||||
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
|
||||
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
|
||||
U+FF0E, U+FF1A-FF1B, U+FFFF;
|
||||
src: url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)
|
||||
format('woff2'),
|
||||
url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)
|
||||
format('woff');
|
||||
}
|
||||
[data-theme-name='spotify'] {
|
||||
--bui-font-text: CircularSp, CircularSp-Arab, CircularSp-Hebr, CircularSp-Cyrl,
|
||||
CircularSp-Grek, CircularSp-Deva;
|
||||
--bui-font-title: CircularSpTitle, CircularSp-Arab, CircularSp-Hebr,
|
||||
CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
|
||||
--bui-font-regular: CircularSp, CircularSp-Arab, CircularSp-Hebr,
|
||||
CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
|
||||
& .bui-Button {
|
||||
border-radius: var(--bui-radius-3);
|
||||
padding-inline: var(--bui-space-3);
|
||||
}
|
||||
& .bui-ButtonIcon {
|
||||
padding: 0;
|
||||
}
|
||||
& .bui-MenuPopup {
|
||||
box-sizing: border-box;
|
||||
max-width: 21.25rem;
|
||||
}
|
||||
& .bui-MenuSubmenuTrigger,
|
||||
& .bui-MenuItem {
|
||||
height: auto;
|
||||
min-height: 2rem;
|
||||
}
|
||||
& .bui-Text {
|
||||
font-family: var(--bui-font-text);
|
||||
}
|
||||
& .bui-Heading {
|
||||
font-family: var(--bui-font-title);
|
||||
}
|
||||
& .bui-TableRow {
|
||||
border-radius: var(--bui-radius-4);
|
||||
border: none;
|
||||
}
|
||||
& .bui-TableRow:hover td:first-child {
|
||||
border-top-left-radius: var(--bui-radius-2);
|
||||
border-bottom-left-radius: var(--bui-radius-2);
|
||||
}
|
||||
& .bui-TableRow:hover td:last-child {
|
||||
border-top-right-radius: var(--bui-radius-2);
|
||||
border-bottom-right-radius: var(--bui-radius-2);
|
||||
}
|
||||
& .bui-TableBody:before,
|
||||
& .bui-TableBody:after {
|
||||
line-height: var(--bui-space-1);
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
& .bui-TableHeader .bui-TableRow {
|
||||
border-bottom: 1px solid var(--bui-border-2);
|
||||
}
|
||||
& .bui-TableHead {
|
||||
font-size: var(--bui-font-size-2);
|
||||
color: var(--bui-fg-secondary);
|
||||
font-weight: var(--bui-font-weight-regular);
|
||||
}
|
||||
& .bui-HeaderToolbar {
|
||||
padding-top: var(--bui-space-2);
|
||||
padding-inline: var(--bui-space-2);
|
||||
}
|
||||
& .bui-HeaderToolbarWrapper {
|
||||
border-radius: var(--bui-radius-3);
|
||||
padding-inline: var(--bui-space-3);
|
||||
border: none;
|
||||
}
|
||||
& .bui-HeaderToolbarControls {
|
||||
right: calc(var(--bui-space-3) - 1px);
|
||||
}
|
||||
& .bui-HeaderTabsWrapper {
|
||||
margin-top: var(--bui-space-2);
|
||||
margin-inline: var(--bui-space-2);
|
||||
border-radius: var(--bui-radius-3);
|
||||
padding-inline: var(--bui-space-1);
|
||||
border: none;
|
||||
}
|
||||
& .bui-Input {
|
||||
border-radius: var(--bui-radius-3);
|
||||
}
|
||||
& .bui-Tag {
|
||||
border-radius: var(--bui-radius-full);
|
||||
}
|
||||
}
|
||||
[data-theme-mode='light'][data-theme-name='spotify'] {
|
||||
--bui-bg-solid: #1ed760;
|
||||
--bui-bg-solid-hover: #3be477;
|
||||
--bui-bg-solid-pressed: #1abc54;
|
||||
--bui-bg-solid-disabled: #0f6c30;
|
||||
--bui-fg-primary: var(--bui-black);
|
||||
--bui-fg-secondary: #757575;
|
||||
--bui-fg-solid: var(--bui-black);
|
||||
--bui-fg-solid-disabled: #62ab7c;
|
||||
--bui-border-2: #d9d9d9;
|
||||
--bui-border-danger: #f87a7a;
|
||||
--bui-border-warning: #e36d05;
|
||||
--bui-border-success: #53db83;
|
||||
--bui-ring: #0003;
|
||||
& .bui-HeaderToolbarWrapper,
|
||||
& .bui-HeaderTabsWrapper {
|
||||
border: 1px solid var(--bui-border-2);
|
||||
}
|
||||
}
|
||||
[data-theme-mode='dark'][data-theme-name='spotify'] {
|
||||
--bui-bg-app: var(--bui-black);
|
||||
--bui-bg-solid: #1ed760;
|
||||
--bui-bg-solid-hover: #3be477;
|
||||
--bui-bg-solid-pressed: #1abc54;
|
||||
--bui-bg-solid-disabled: #0f6c30;
|
||||
--bui-bg-danger: #3b1219;
|
||||
--bui-bg-warning: #302008;
|
||||
--bui-bg-success: #132d21;
|
||||
--bui-fg-primary: var(--bui-white);
|
||||
--bui-fg-secondary: #9e9e9e;
|
||||
--bui-fg-disabled: #575757;
|
||||
--bui-fg-solid: var(--bui-black);
|
||||
--bui-fg-solid-disabled: #072f15;
|
||||
--bui-fg-danger: #e22b2b;
|
||||
--bui-fg-warning: #e36d05;
|
||||
--bui-fg-success: #1db954;
|
||||
--bui-border-2: #373737;
|
||||
--bui-border-danger: #f87a7a;
|
||||
--bui-border-warning: #e36d05;
|
||||
--bui-border-success: #53db83;
|
||||
--bui-ring: #fff3;
|
||||
}
|
||||
@import '../../../.storybook/themes/spotify.css';
|
||||
|
||||
+77
-857
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user