chore: scaffold SPFx solution with ui-kit
Clean SPFx 1.22 (heft) web part template with @tnsu/ui-kit wired in. Starting point for merging the stroy-bot-* repositories. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+321
@@ -0,0 +1,321 @@
|
|||||||
|
require('@rushstack/eslint-config/patch/modern-module-resolution');
|
||||||
|
module.exports = {
|
||||||
|
extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
|
||||||
|
parserOptions: { tsconfigRootDir: __dirname },
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
'parserOptions': {
|
||||||
|
'project': './tsconfig.json',
|
||||||
|
'ecmaVersion': 2018,
|
||||||
|
'sourceType': 'module'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Prevent usage of the JavaScript null value, while allowing code to access existing APIs that may require null. https://www.npmjs.com/package/@rushstack/eslint-plugin
|
||||||
|
'@rushstack/no-new-null': 1,
|
||||||
|
// Require Jest module mocking APIs to be called before any other statements in their code block. https://www.npmjs.com/package/@rushstack/eslint-plugin
|
||||||
|
'@rushstack/hoist-jest-mock': 1,
|
||||||
|
// Require chunk names for dynamic imports in SPFx projects. https://www.npmjs.com/package/@rushstack/eslint-plugin
|
||||||
|
'@rushstack/import-requires-chunk-name': 1,
|
||||||
|
// Ensure that React components rendered with ReactDOM.render() are unmounted with ReactDOM.unmountComponentAtNode(). https://www.npmjs.com/package/@rushstack/eslint-plugin
|
||||||
|
'@rushstack/pair-react-dom-render-unmount': 1,
|
||||||
|
// Require regular expressions to be constructed from string constants rather than dynamically building strings at runtime. https://www.npmjs.com/package/@rushstack/eslint-plugin-security
|
||||||
|
'@rushstack/security/no-unsafe-regexp': 1,
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/adjacent-overload-signatures': 1,
|
||||||
|
// RATIONALE: Code is more readable when the type of every variable is immediately obvious.
|
||||||
|
// Even if the compiler may be able to infer a type, this inference will be unavailable
|
||||||
|
// to a person who is reviewing a GitHub diff. This rule makes writing code harder,
|
||||||
|
// but writing code is a much less important activity than reading it.
|
||||||
|
//
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/explicit-function-return-type': [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
'allowExpressions': true,
|
||||||
|
'allowTypedFunctionExpressions': true,
|
||||||
|
'allowHigherOrderFunctions': false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
// Rationale to disable: although this is a recommended rule, it is up to dev to select coding style.
|
||||||
|
// Set to 1 (warning) or 2 (error) to enable.
|
||||||
|
'@typescript-eslint/explicit-member-accessibility': 0,
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/no-array-constructor': 1,
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
//
|
||||||
|
// RATIONALE: The "any" keyword disables static type checking, the main benefit of using TypeScript.
|
||||||
|
// This rule should be suppressed only in very special cases such as JSON.stringify()
|
||||||
|
// where the type really can be anything. Even if the type is flexible, another type
|
||||||
|
// may be more appropriate such as "unknown", "{}", or "Record<k,V>".
|
||||||
|
'@typescript-eslint/no-explicit-any': 1,
|
||||||
|
// RATIONALE: The #1 rule of promises is that every promise chain must be terminated by a catch()
|
||||||
|
// handler. Thus wherever a Promise arises, the code must either append a catch handler,
|
||||||
|
// or else return the object to a caller (who assumes this responsibility). Unterminated
|
||||||
|
// promise chains are a serious issue. Besides causing errors to be silently ignored,
|
||||||
|
// they can also cause a NodeJS process to terminate unexpectedly.
|
||||||
|
'@typescript-eslint/no-floating-promises': 2,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'@typescript-eslint/no-for-in-array': 2,
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/no-misused-new': 2,
|
||||||
|
// RATIONALE: The "namespace" keyword is not recommended for organizing code because JavaScript lacks
|
||||||
|
// a "using" statement to traverse namespaces. Nested namespaces prevent certain bundler
|
||||||
|
// optimizations. If you are declaring loose functions/variables, it's better to make them
|
||||||
|
// static members of a class, since classes support property getters and their private
|
||||||
|
// members are accessible by unit tests. Also, the exercise of choosing a meaningful
|
||||||
|
// class name tends to produce more discoverable APIs: for example, search+replacing
|
||||||
|
// the function "reverse()" is likely to return many false matches, whereas if we always
|
||||||
|
// write "Text.reverse()" is more unique. For large scale organization, it's recommended
|
||||||
|
// to decompose your code into separate NPM packages, which ensures that component
|
||||||
|
// dependencies are tracked more conscientiously.
|
||||||
|
//
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/no-namespace': [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
'allowDeclarations': false,
|
||||||
|
'allowDefinitionFiles': false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// RATIONALE: Parameter properties provide a shorthand such as "constructor(public title: string)"
|
||||||
|
// that avoids the effort of declaring "title" as a field. This TypeScript feature makes
|
||||||
|
// code easier to write, but arguably sacrifices readability: In the notes for
|
||||||
|
// "@typescript-eslint/member-ordering" we pointed out that fields are central to
|
||||||
|
// a class's design, so we wouldn't want to bury them in a constructor signature
|
||||||
|
// just to save some typing.
|
||||||
|
//
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
// Set to 1 (warning) or 2 (error) to enable the rule
|
||||||
|
'@typescript-eslint/parameter-properties': 0,
|
||||||
|
// RATIONALE: When left in shipping code, unused variables often indicate a mistake. Dead code
|
||||||
|
// may impact performance.
|
||||||
|
//
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
'vars': 'all',
|
||||||
|
// Unused function arguments often indicate a mistake in JavaScript code. However in TypeScript code,
|
||||||
|
// the compiler catches most of those mistakes, and unused arguments are fairly common for type signatures
|
||||||
|
// that are overriding a base class method or implementing an interface.
|
||||||
|
'args': 'none'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/no-use-before-define': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
'functions': false,
|
||||||
|
'classes': true,
|
||||||
|
'variables': true,
|
||||||
|
'enums': true,
|
||||||
|
'typedefs': true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// Disallows require statements except in import statements.
|
||||||
|
// In other words, the use of forms such as var foo = require("foo") are banned. Instead use ES6 style imports or import foo = require("foo") imports.
|
||||||
|
'@typescript-eslint/no-var-requires': 'error',
|
||||||
|
// RATIONALE: The "module" keyword is deprecated except when describing legacy libraries.
|
||||||
|
//
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'@typescript-eslint/prefer-namespace-keyword': 1,
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
// Rationale to disable: it's up to developer to decide if he wants to add type annotations
|
||||||
|
// Set to 1 (warning) or 2 (error) to enable the rule
|
||||||
|
'@typescript-eslint/no-inferrable-types': 0,
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
// Rationale to disable: declaration of empty interfaces may be helpful for generic types scenarios
|
||||||
|
'@typescript-eslint/no-empty-interface': 0,
|
||||||
|
// RATIONALE: This rule warns if setters are defined without getters, which is probably a mistake.
|
||||||
|
'accessor-pairs': 1,
|
||||||
|
// RATIONALE: In TypeScript, if you write x["y"] instead of x.y, it disables type checking.
|
||||||
|
'dot-notation': [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
'allowPattern': '^_'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
'eqeqeq': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'for-direction': 1,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'guard-for-in': 2,
|
||||||
|
// RATIONALE: If you have more than 2,000 lines in a single source file, it's probably time
|
||||||
|
// to split up your code.
|
||||||
|
'max-lines': ['warn', { max: 2000 }],
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-async-promise-executor': 2,
|
||||||
|
// RATIONALE: Deprecated language feature.
|
||||||
|
'no-caller': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-compare-neg-zero': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-cond-assign': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-constant-condition': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-control-regex': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-debugger': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-delete-var': 2,
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-duplicate-case': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-empty': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-empty-character-class': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-empty-pattern': 1,
|
||||||
|
// RATIONALE: Eval is a security concern and a performance concern.
|
||||||
|
'no-eval': 1,
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-ex-assign': 2,
|
||||||
|
// RATIONALE: System types are global and should not be tampered with in a scalable code base.
|
||||||
|
// If two different libraries (or two versions of the same library) both try to modify
|
||||||
|
// a type, only one of them can win. Polyfills are acceptable because they implement
|
||||||
|
// a standardized interoperable contract, but polyfills are generally coded in plain
|
||||||
|
// JavaScript.
|
||||||
|
'no-extend-native': 1,
|
||||||
|
// Disallow unnecessary labels
|
||||||
|
'no-extra-label': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-fallthrough': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-func-assign': 1,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'no-implied-eval': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-invalid-regexp': 2,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'no-label-var': 2,
|
||||||
|
// RATIONALE: Eliminates redundant code.
|
||||||
|
'no-lone-blocks': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-misleading-character-class': 2,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'no-multi-str': 2,
|
||||||
|
// RATIONALE: It's generally a bad practice to call "new Thing()" without assigning the result to
|
||||||
|
// a variable. Either it's part of an awkward expression like "(new Thing()).doSomething()",
|
||||||
|
// or else implies that the constructor is doing nontrivial computations, which is often
|
||||||
|
// a poor class design.
|
||||||
|
'no-new': 1,
|
||||||
|
// RATIONALE: Obsolete language feature that is deprecated.
|
||||||
|
'no-new-func': 2,
|
||||||
|
// RATIONALE: Obsolete language feature that is deprecated.
|
||||||
|
'no-new-object': 2,
|
||||||
|
// RATIONALE: Obsolete notation.
|
||||||
|
'no-new-wrappers': 1,
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-octal': 2,
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
'no-octal-escape': 2,
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-regex-spaces': 2,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'no-return-assign': 2,
|
||||||
|
// RATIONALE: Security risk.
|
||||||
|
'no-script-url': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-self-assign': 2,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'no-self-compare': 2,
|
||||||
|
// RATIONALE: This avoids statements such as "while (a = next(), a && a.length);" that use
|
||||||
|
// commas to create compound expressions. In general code is more readable if each
|
||||||
|
// step is split onto a separate line. This also makes it easier to set breakpoints
|
||||||
|
// in the debugger.
|
||||||
|
'no-sequences': 1,
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-shadow-restricted-names': 2,
|
||||||
|
// RATIONALE: Obsolete language feature that is deprecated.
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-sparse-arrays': 2,
|
||||||
|
// RATIONALE: Although in theory JavaScript allows any possible data type to be thrown as an exception,
|
||||||
|
// such flexibility adds pointless complexity, by requiring every catch block to test
|
||||||
|
// the type of the object that it receives. Whereas if catch blocks can always assume
|
||||||
|
// that their object implements the "Error" contract, then the code is simpler, and
|
||||||
|
// we generally get useful additional information like a call stack.
|
||||||
|
'no-throw-literal': 2,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'no-unmodified-loop-condition': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-unsafe-finally': 2,
|
||||||
|
// RATIONALE: Catches a common coding mistake.
|
||||||
|
'no-unused-expressions': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-unused-labels': 1,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-useless-catch': 1,
|
||||||
|
// RATIONALE: Avoids a potential performance problem.
|
||||||
|
'no-useless-concat': 1,
|
||||||
|
// RATIONALE: The "var" keyword is deprecated because of its confusing "hoisting" behavior.
|
||||||
|
// Always use "let" or "const" instead.
|
||||||
|
//
|
||||||
|
// STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json
|
||||||
|
'no-var': 2,
|
||||||
|
// RATIONALE: Generally not needed in modern code.
|
||||||
|
'no-void': 1,
|
||||||
|
// RATIONALE: Obsolete language feature that is deprecated.
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'no-with': 2,
|
||||||
|
// RATIONALE: Makes logic easier to understand, since constants always have a known value
|
||||||
|
// @typescript-eslint\eslint-plugin\dist\configs\eslint-recommended.js
|
||||||
|
'prefer-const': 1,
|
||||||
|
// RATIONALE: Catches a common coding mistake where "resolve" and "reject" are confused.
|
||||||
|
'promise/param-names': 2,
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'require-atomic-updates': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'require-yield': 1,
|
||||||
|
// "Use strict" is redundant when using the TypeScript compiler.
|
||||||
|
'strict': [
|
||||||
|
2,
|
||||||
|
'never'
|
||||||
|
],
|
||||||
|
// RATIONALE: Catches code that is likely to be incorrect
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
'use-isnan': 2,
|
||||||
|
// STANDARDIZED BY: eslint\conf\eslint-recommended.js
|
||||||
|
// Set to 1 (warning) or 2 (error) to enable.
|
||||||
|
// Rationale to disable: !!{}
|
||||||
|
'no-extra-boolean-cast': 0,
|
||||||
|
// ====================================================================
|
||||||
|
// @microsoft/eslint-plugin-spfx
|
||||||
|
// ====================================================================
|
||||||
|
'@microsoft/spfx/no-require-ensure': 2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// For unit tests, we can be a little bit less strict. The settings below revise the
|
||||||
|
// defaults specified in the extended configurations, as well as above.
|
||||||
|
files: [
|
||||||
|
// Test files
|
||||||
|
'*.test.ts',
|
||||||
|
'*.test.tsx',
|
||||||
|
'*.spec.ts',
|
||||||
|
'*.spec.tsx',
|
||||||
|
|
||||||
|
// Facebook convention
|
||||||
|
'**/__mocks__/*.ts',
|
||||||
|
'**/__mocks__/*.tsx',
|
||||||
|
'**/__tests__/*.ts',
|
||||||
|
'**/__tests__/*.tsx',
|
||||||
|
|
||||||
|
// Microsoft convention
|
||||||
|
'**/test/*.ts',
|
||||||
|
'**/test/*.tsx'
|
||||||
|
],
|
||||||
|
rules: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
node_modules/
|
||||||
|
lib/
|
||||||
|
dist/
|
||||||
|
temp/
|
||||||
|
*.sppkg
|
||||||
|
.heft/
|
||||||
|
sharepoint/solution/
|
||||||
|
lib-commonjs/
|
||||||
|
release/
|
||||||
|
jest-output/
|
||||||
|
.claude/settings.local.json
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
# Ignore everything by default
|
||||||
|
**
|
||||||
|
|
||||||
|
# Use negative patterns to bring back the specific things we want to publish
|
||||||
|
!/bin/**
|
||||||
|
!/dist/**
|
||||||
|
!/EULA/**
|
||||||
|
!/lib/**
|
||||||
|
!ThirdPartyNotice.txt
|
||||||
|
|
||||||
|
# Ignore certain files in the above folders
|
||||||
|
/dist/*.stats.*
|
||||||
|
/dist/**/*.js.map
|
||||||
|
/lib/**/*.js.map
|
||||||
|
/lib/**/test/**
|
||||||
|
|
||||||
|
# NOTE: These don't need to be specified, because NPM includes them automatically.
|
||||||
|
#
|
||||||
|
# package.json
|
||||||
|
# README (and its variants)
|
||||||
|
# CHANGELOG (and its variants)
|
||||||
|
# LICENSE / LICENCE
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@tnsu:registry=http://git.tnsu.kz/api/packages/timur.gorodetskiy/npm/
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 120,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"semi": true,
|
||||||
|
"endOfLine": "auto"
|
||||||
|
}
|
||||||
Vendored
+23
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Hosted workbench",
|
||||||
|
"type": "msedge",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "https://{tenantDomain}/_layouts/workbench.aspx",
|
||||||
|
"webRoot": "${workspaceRoot}",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"sourceMapPathOverrides": {
|
||||||
|
"webpack:///.././src/*": "${webRoot}/src/*",
|
||||||
|
"webpack:///../../../src/*": "${webRoot}/src/*",
|
||||||
|
"webpack:///../../../../src/*": "${webRoot}/src/*",
|
||||||
|
"webpack:///../../../../../src/*": "${webRoot}/src/*"
|
||||||
|
},
|
||||||
|
"runtimeArgs": [
|
||||||
|
"--remote-debugging-port=9222",
|
||||||
|
"-incognito"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
|
{
|
||||||
|
// Configure glob patterns for excluding files and folders in the file explorer.
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.git": true,
|
||||||
|
"**/.DS_Store": true,
|
||||||
|
"**/bower_components": true,
|
||||||
|
"**/coverage": true,
|
||||||
|
"**/jest-output": true,
|
||||||
|
"**/lib-amd": true,
|
||||||
|
"src/**/*.scss.ts": true
|
||||||
|
},
|
||||||
|
"typescript.tsdk": ".\\node_modules\\typescript\\lib"
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"@microsoft/generator-sharepoint": {
|
||||||
|
"useGulp": false,
|
||||||
|
"plusBeta": false,
|
||||||
|
"isCreatingSolution": true,
|
||||||
|
"nodeVersion": "22.22.2",
|
||||||
|
"sdksVersions": {},
|
||||||
|
"version": "1.22.2",
|
||||||
|
"libraryName": "stroy-bot-monorepo",
|
||||||
|
"libraryId": "69f41a0d-e051-4156-b6b1-abc6ea38ca36",
|
||||||
|
"environment": "spo",
|
||||||
|
"packageManager": "npm",
|
||||||
|
"solutionName": "stroy-bot-monorepo",
|
||||||
|
"solutionShortDescription": "stroy-bot-monorepo description",
|
||||||
|
"skipFeatureDeployment": true,
|
||||||
|
"isDomainIsolated": false,
|
||||||
|
"componentType": "webpart"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
Ты — опытный Senior Frontend Engineer.
|
||||||
|
Твоя специализация: React (TypeScript), корпоративные веб‑приложения, порталы и SPA.
|
||||||
|
|
||||||
|
ОБЩИЕ ПРИНЦИПЫ
|
||||||
|
- Пиши код так, как если бы он шёл в production.
|
||||||
|
- Предпочитай простые и читаемые решения.
|
||||||
|
- Избегай overengineering.
|
||||||
|
- Всегда думай о поддерживаемости кода через 1–2 года.
|
||||||
|
- Не выдумывай API, props или окружение — используй только то, что явно указано.
|
||||||
|
- Если информации недостаточно — делай разумные предположения и явно их помечай.
|
||||||
|
|
||||||
|
СТЕК И СИНТАКСИС
|
||||||
|
- React (Function Components)
|
||||||
|
- TypeScript (строгая типизация, без any)
|
||||||
|
- Hooks вместо классов
|
||||||
|
- JSX / TSX
|
||||||
|
- CSS Modules или SCSS Modules
|
||||||
|
- Без deprecated API
|
||||||
|
|
||||||
|
СТРУКТУРА КОДА
|
||||||
|
- Один компонент — один файл
|
||||||
|
- Явные интерфейсы props (`interface Props {}`)
|
||||||
|
- Логика отделена от представления
|
||||||
|
- Минимум вложенности
|
||||||
|
- Ранние return’ы вместо глубоких if
|
||||||
|
|
||||||
|
РАЗМЕР И ДЕКОМПОЗИЦИЯ
|
||||||
|
- Компонент — не больше 300 строк. Больше — сигнал, что пора разбивать.
|
||||||
|
- Функция / хук — не больше ~50 строк, делает одну вещь.
|
||||||
|
- Файл делает что-то одно: не смешивай несколько компонентов в одном файле.
|
||||||
|
- Не больше 3–4 уровней вложенности JSX — глубже выноси в под-компонент.
|
||||||
|
- Props: до ~7 штук. Больше — сгруппируй в объект или пересмотри разбивку.
|
||||||
|
- Большой компонент дели по смыслу: под-компоненты (`Foo.tsx` → `FooHeader.tsx`, `FooRow.tsx`), кастомные хуки для логики (`useFooData.ts`), утилиты (`utils/`).
|
||||||
|
- Повторяешь один и тот же блок 3-й раз — выноси в общий компонент/функцию.
|
||||||
|
|
||||||
|
ИМЕНА И СТИЛЬ
|
||||||
|
- Компоненты: PascalCase
|
||||||
|
- Хуки: useSomething
|
||||||
|
- Переменные: camelCase
|
||||||
|
- CSS‑классы: kebab-case или camelCase (последовательно)
|
||||||
|
- Названия должны отражать смысл, а не реализацию
|
||||||
|
|
||||||
|
REACT‑ПРАКТИКИ
|
||||||
|
- useEffect — только для сайд‑эффектов
|
||||||
|
- Не клади бизнес‑логику в JSX
|
||||||
|
- Используй useMemo / useCallback только при реальной необходимости
|
||||||
|
- Контролируемые компоненты предпочтительнее неконтролируемых
|
||||||
|
- Не мутируй props и state
|
||||||
|
|
||||||
|
УПРАВЛЕНИЕ СОСТОЯНИЕМ
|
||||||
|
- Состояние живёт как можно ниже; поднимай наверх только то, что реально общее.
|
||||||
|
- State многошаговой формы держи в одном родителе (например, `CtCreateForm`) — шаги получают срез данных и колбэки, а не тянут состояние сами.
|
||||||
|
- Prop-drilling глубже 2–3 уровней — сигнал вынести общее в React Context.
|
||||||
|
- Context — для по-настоящему сквозных данных (текущая СТ, пользователь, справочники), не для всего подряд.
|
||||||
|
- Один источник истины: не дублируй одни и те же данные в нескольких useState.
|
||||||
|
- Изменяй state иммутабельно (`setItems(prev => [...prev, x])`), без мутаций массивов/объектов.
|
||||||
|
- Производные данные считай на лету (useMemo), не храни в отдельном state.
|
||||||
|
|
||||||
|
LAYOUT И ВЁРСТКА
|
||||||
|
- Верстка адаптивная
|
||||||
|
- Используй flex / grid
|
||||||
|
- Контейнеры по умолчанию занимают 100% доступной ширины
|
||||||
|
- Не используй magic‑numbers без комментариев
|
||||||
|
- Padding и margin — консистентные
|
||||||
|
|
||||||
|
КОНСТАНТЫ И СТРОКИ
|
||||||
|
- Никаких magic-strings в логике: статусы, ключи и имена SP-списков, роуты — в константах.
|
||||||
|
- Повторяющиеся значения выноси в один модуль констант (`constants.ts`), а не хардкодь по месту.
|
||||||
|
- Статусы и перечисления — через union-типы или enum, чтобы опечатка ловилась компилятором.
|
||||||
|
- Числовые «магические» значения — именованные константы с понятным смыслом.
|
||||||
|
- Пользовательские тексты собирай в одном месте — задел под локализацию, чтобы строки не были размазаны по JSX.
|
||||||
|
- URL, id ресурсов и настройки окружения — не в коде компонентов, а в конфиге/константах.
|
||||||
|
|
||||||
|
КОММЕНТАРИИ
|
||||||
|
- Комментируй ПОЧЕМУ, а не ЧТО
|
||||||
|
- Не дублируй комментариями очевидный код
|
||||||
|
- Для сложной логики — короткий поясняющий комментарий
|
||||||
|
|
||||||
|
ОШИБКИ И СОСТОЯНИЯ
|
||||||
|
- Явно обрабатывай loading / error / empty state
|
||||||
|
- Не скрывай ошибки
|
||||||
|
- UI должен корректно вести себя при отсутствии данных
|
||||||
|
|
||||||
|
GIT И КОММИТЫ
|
||||||
|
- Формат: `тип: краткое описание в настоящем времени` (`feat:`, `fix:`, `refactor:`, `chore:`, `docs:`, `test:`).
|
||||||
|
- Один коммит — одно логическое изменение, без свалки несвязанных правок.
|
||||||
|
- Сообщение объясняет ЧТО и ЗАЧЕМ, а не пересказывает диф.
|
||||||
|
- Не коммить закомментированный/отладочный код и секреты.
|
||||||
|
- Пиши так, чтобы по истории джун понял ход работы без расспросов.
|
||||||
|
|
||||||
|
ФОРМАТ ОТВЕТА
|
||||||
|
- Если даёшь код — давай ПОЛНЫЙ пример, а не фрагмент
|
||||||
|
- Если есть несколько вариантов — объясни, почему выбран именно этот
|
||||||
|
- Не пиши лишний текст вокруг кода
|
||||||
|
- Ответы краткие, структурированные, по делу
|
||||||
|
|
||||||
|
ПОДДЕРЖИВАЕМОСТЬ (ЧТОБЫ ДЖУН МОГ ПОДХВАТИТЬ)
|
||||||
|
- Пиши так, будто проект дальше ведёт джун без твоего контекста.
|
||||||
|
- Никакой «магии» и умных однострочников — понятный код важнее короткого.
|
||||||
|
- Понятные имена вместо комментариев-костылей: `isLoading`, а не `flag`.
|
||||||
|
- Предпочитай явное неявному: не полагайся на неочевидные побочные эффекты.
|
||||||
|
- Структура папок предсказуемая и одинаковая по всему проекту.
|
||||||
|
- Типы описывают данные явно — джун должен понять форму объекта, не запуская код.
|
||||||
|
- Сложную бизнес-логику сопровождай коротким «почему» в комментарии.
|
||||||
|
- Не оставляй мёртвый/закомментированный код — удаляй.
|
||||||
|
|
||||||
|
КОНТЕКСТ
|
||||||
|
- Код пишется для корпоративного портала
|
||||||
|
- Основной приоритет: читаемость, стабильность, предсказуемость
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# stroy-bot-monorepo
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Short summary on functionality and used technologies.
|
||||||
|
|
||||||
|
[picture of the solution in action, if possible]
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
- [SharePoint Framework](https://aka.ms/spfx)
|
||||||
|
- [Microsoft 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||||
|
|
||||||
|
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
> Any special pre-requisites?
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
| Solution | Author(s) |
|
||||||
|
| ----------- | ------------------------------------------------------- |
|
||||||
|
| folder name | Author details (name, company, twitter alias with link) |
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
| Version | Date | Comments |
|
||||||
|
| ------- | ---------------- | --------------- |
|
||||||
|
| 1.1 | March 10, 2021 | Update comment |
|
||||||
|
| 1.0 | January 29, 2021 | Initial release |
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
**THIS CODE IS PROVIDED _AS IS_ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
- Clone this repository
|
||||||
|
- Ensure that you are at the solution folder
|
||||||
|
- in the command-line run:
|
||||||
|
- `npm install -g @rushstack/heft`
|
||||||
|
- `npm install`
|
||||||
|
- `heft start`
|
||||||
|
|
||||||
|
> Include any additional steps as needed.
|
||||||
|
|
||||||
|
Other build commands can be listed using `heft --help`.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
Description of the extension that expands upon high-level summary above.
|
||||||
|
|
||||||
|
This extension illustrates the following concepts:
|
||||||
|
|
||||||
|
- topic 1
|
||||||
|
- topic 2
|
||||||
|
- topic 3
|
||||||
|
|
||||||
|
> Notice that better pictures and documentation will increase the sample usage and the value you are providing for others. Thanks for your submissions advance.
|
||||||
|
|
||||||
|
> Share your web part with others through Microsoft 365 Patterns and Practices program to get visibility and exposure. More details on the community, open-source projects and other activities from http://aka.ms/m365pnp.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Getting started with SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||||
|
- [Building for Microsoft teams](https://docs.microsoft.com/sharepoint/dev/spfx/build-for-teams-overview)
|
||||||
|
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
|
||||||
|
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/sharepoint/dev/spfx/publish-to-marketplace-overview)
|
||||||
|
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
|
||||||
|
- [Heft Documentation](https://heft.rushstack.io/)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"stroy-bot-monorepo-web-part": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/stroyBotMonorepo/StroyBotMonorepoWebPart.js",
|
||||||
|
"manifest": "./src/webparts/stroyBotMonorepo/StroyBotMonorepoWebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"StroyBotMonorepoWebPartStrings": "lib/webparts/stroyBotMonorepo/loc/{locale}.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||||
|
"workingDir": "./release/assets/",
|
||||||
|
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||||
|
"container": "stroy-bot-monorepo",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "stroy-bot-monorepo-client-side-solution",
|
||||||
|
"id": "69f41a0d-e051-4156-b6b1-abc6ea38ca36",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"includeClientSideAssets": true,
|
||||||
|
"skipFeatureDeployment": true,
|
||||||
|
"isDomainIsolated": false,
|
||||||
|
"developer": {
|
||||||
|
"name": "",
|
||||||
|
"websiteUrl": "",
|
||||||
|
"privacyUrl": "",
|
||||||
|
"termsOfUseUrl": "",
|
||||||
|
"mpnId": "Undefined-1.22.2"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"shortDescription": {
|
||||||
|
"default": "stroy-bot-monorepo description"
|
||||||
|
},
|
||||||
|
"longDescription": {
|
||||||
|
"default": "stroy-bot-monorepo description"
|
||||||
|
},
|
||||||
|
"screenshotPaths": [],
|
||||||
|
"videoUrl": "",
|
||||||
|
"categories": []
|
||||||
|
},
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"title": "stroy-bot-monorepo Feature",
|
||||||
|
"description": "The feature that activates elements of the stroy-bot-monorepo solution.",
|
||||||
|
"id": "16e4e1fd-bc96-4e65-a3ea-01b5c50d36dc",
|
||||||
|
"version": "1.0.0.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/stroy-bot-monorepo.sppkg"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||||
|
"rigPackageName": "@microsoft/spfx-web-build-rig"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json",
|
||||||
|
"extends": "@microsoft/spfx-web-build-rig/profiles/default/config/sass.json"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
|
||||||
|
"port": 4321,
|
||||||
|
"https": true,
|
||||||
|
"initialPage": "https://tnsukz.sharepoint.com/_layouts/workbench.aspx"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "@microsoft/spfx-web-build-rig/profiles/default/config/typescript.json",
|
||||||
|
"staticAssetsToCopy": {
|
||||||
|
"fileExtensions": [".resx", ".jpg", ".png", ".woff", ".eot", ".ttf", ".svg", ".gif"],
|
||||||
|
"includeGlobs": ["webparts/*/loc/*.js"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||||
|
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||||
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
# =============================================================
|
||||||
|
# SPFx + Heft project bootstrap script (PowerShell)
|
||||||
|
# ВАРИАНТ С ПОДКЛЮЧЕНИЕМ ОБЩЕЙ UI-БИБЛИОТЕКИ @tnsu/ui-kit.
|
||||||
|
# Запустите из корня вашей ПУСТОЙ папки проекта:
|
||||||
|
# .\create-spfx-uikit.ps1
|
||||||
|
#
|
||||||
|
# Если запуск скриптов заблокирован, выполните сначала:
|
||||||
|
# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
||||||
|
#
|
||||||
|
# Требование: у запускающего должен быть токен @tnsu в ~/.npmrc
|
||||||
|
# (см. README пакета tnsu-ui-kit) — иначе установка @tnsu/ui-kit не пройдёт.
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# ---- Цвета вывода ----
|
||||||
|
function Info { param($msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan }
|
||||||
|
function Success { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green }
|
||||||
|
function Warn { param($msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow }
|
||||||
|
function Header { param($msg) Write-Host "`n$msg" -ForegroundColor White }
|
||||||
|
|
||||||
|
Header "================================================="
|
||||||
|
Header " SPFx + Heft — автоматическая инициализация"
|
||||||
|
Header " (с общей UI-библиотекой @tnsu/ui-kit)"
|
||||||
|
Header "=================================================`n"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 1. Проверка Node.js (SPFx 1.20+ требует Node 18+)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Проверка Node.js..."
|
||||||
|
|
||||||
|
try {
|
||||||
|
$nodeVersion = node -e "process.stdout.write(process.version)" 2>&1
|
||||||
|
} catch {
|
||||||
|
Warn "Node.js не найден. Установите Node 18 LTS: https://nodejs.org"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$nodeMajor = [int]($nodeVersion.TrimStart('v').Split('.')[0])
|
||||||
|
if ($nodeMajor -lt 18) {
|
||||||
|
Warn "Обнаружен Node.js $nodeVersion. SPFx 1.20+ требует Node 18.x или выше."
|
||||||
|
Warn "Установите Node 18 LTS через nvm-windows или с https://nodejs.org"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Success "Node.js $nodeVersion — подходит."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 2. Вспомогательная функция: установка глобального пакета
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
function Install-GlobalIfMissing {
|
||||||
|
param(
|
||||||
|
[string]$Package,
|
||||||
|
[string]$Command = ""
|
||||||
|
)
|
||||||
|
if ($Command -eq "") { $Command = $Package.Split('/')[-1] }
|
||||||
|
|
||||||
|
if (-not (Get-Command $Command -ErrorAction SilentlyContinue)) {
|
||||||
|
Info "Устанавливаем $Package глобально..."
|
||||||
|
npm install -g $Package
|
||||||
|
Success "$Package установлен."
|
||||||
|
} else {
|
||||||
|
Success "$Package уже установлен."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Install-GlobalIfMissing "@microsoft/generator-sharepoint" "yo"
|
||||||
|
Install-GlobalIfMissing "yo" "yo"
|
||||||
|
Install-GlobalIfMissing "@rushstack/heft" "heft"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 3. Создание package.json
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Создаём package.json..."
|
||||||
|
|
||||||
|
$packageJson = @'
|
||||||
|
{
|
||||||
|
"name": "spfx-heft-project",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "heft build",
|
||||||
|
"bundle": "heft build && gulp bundle --ship",
|
||||||
|
"package-solution": "heft build && gulp package-solution --ship",
|
||||||
|
"serve": "heft build && gulp serve",
|
||||||
|
"clean": "heft clean"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"ajv": "^8.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
|
||||||
|
$packageJson | Set-Content -Encoding UTF8 "package.json"
|
||||||
|
Success "package.json создан."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 4. Создание структуры папок
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Создаём структуру папок..."
|
||||||
|
New-Item -ItemType Directory -Force -Path "config" | Out-Null
|
||||||
|
New-Item -ItemType Directory -Force -Path "src\webparts" | Out-Null
|
||||||
|
Success "Папки созданы."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 5. Запуск Yeoman-генератора SPFx
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Запускаем Yeoman-генератор SPFx..."
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host ">>> Сейчас откроется интерактивный генератор Yeoman." -ForegroundColor Yellow
|
||||||
|
Write-Host ">>> Выберите тип: WebPart, Framework: No framework (или React)." -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
yo "@microsoft/sharepoint"
|
||||||
|
|
||||||
|
npm uninstall @microsoft/sp-build-web ajv gulp
|
||||||
|
npm uninstall @microsoft/rush-stack-compiler-5.3
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 6. Установка Heft и Rush Stack зависимостей
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Устанавливаем Heft и плагины..."
|
||||||
|
|
||||||
|
$devDeps = @(
|
||||||
|
"@microsoft/spfx-web-build-rig@1.22.1",
|
||||||
|
"@microsoft/spfx-heft-plugins@1.22.1",
|
||||||
|
"@microsoft/eslint-config-spfx@1.22.1",
|
||||||
|
"@microsoft/eslint-plugin-spfx@1.22.17",
|
||||||
|
"@microsoft/sp-module-interfaces@1.22.1",
|
||||||
|
"@rushstack/eslint-config@4.5.2",
|
||||||
|
"@rushstack/heft@1.1.2",
|
||||||
|
"@types/heft-jest@1.0.2",
|
||||||
|
"@typescript-eslint/parser@8.46.2"
|
||||||
|
)
|
||||||
|
|
||||||
|
npm install --save-dev @($devDeps) --save-exact --force --legacy-peer-deps
|
||||||
|
npm install typescript@~5.8.0 --save-dev
|
||||||
|
|
||||||
|
Success "Heft и плагины установлены."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 7. config/rig.json
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Создаём config\rig.json..."
|
||||||
|
New-Item -ItemType Directory -Force -Path "config" | Out-Null
|
||||||
|
|
||||||
|
$rigJson = @'
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||||
|
"rigPackageName": "@microsoft/spfx-web-build-rig"
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
|
||||||
|
$rigJson | Set-Content -Encoding UTF8 "config\rig.json"
|
||||||
|
Success "config\rig.json создан."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 8. config/sass.json
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Создаём config\sass.json..."
|
||||||
|
New-Item -ItemType Directory -Force -Path "config" | Out-Null
|
||||||
|
|
||||||
|
$sassJson = @'
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json",
|
||||||
|
"extends": "@microsoft/spfx-web-build-rig/profiles/default/config/sass.json"
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
|
||||||
|
$sassJson | Set-Content -Encoding UTF8 "config\sass.json"
|
||||||
|
Success "config\sass.json создан."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 9. config/typescript.json
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Создаём config\typescript.json..."
|
||||||
|
New-Item -ItemType Directory -Force -Path "config" | Out-Null
|
||||||
|
|
||||||
|
$typescriptJson = @'
|
||||||
|
{
|
||||||
|
"extends": "@microsoft/spfx-web-build-rig/profiles/default/config/typescript.json",
|
||||||
|
"staticAssetsToCopy": {
|
||||||
|
"fileExtensions": [".resx", ".jpg", ".png", ".woff", ".eot", ".ttf", ".svg", ".gif"],
|
||||||
|
"includeGlobs": ["webparts/*/loc/*.js"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
|
||||||
|
$typescriptJson | Set-Content -Encoding UTF8 "config\typescript.json"
|
||||||
|
Success "config\typescript.json создан."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 8. tsconfig.json (если Yeoman ещё не создал)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if (-not (Test-Path "tsconfig.json")) {
|
||||||
|
Info "Создаём tsconfig.json..."
|
||||||
|
|
||||||
|
$tsConfig = @'
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/@microsoft/spfx-web-build-rig/profiles/default/tsconfig-base.json"
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
|
||||||
|
$tsConfig | Set-Content -Encoding UTF8 "tsconfig.json"
|
||||||
|
Success "tsconfig.json создан."
|
||||||
|
} else {
|
||||||
|
Success "tsconfig.json уже существует, пропускаем."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 9. .eslintrc.js (если не создан)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if (-not (Test-Path ".eslintrc.js")) {
|
||||||
|
Info "Создаём .eslintrc.js..."
|
||||||
|
|
||||||
|
$eslintConfig = @'
|
||||||
|
require('@rushstack/eslint-config/patch/modern-module-resolution');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
extends: ['@microsoft/eslint-config-spfx/lib/profiles/default'],
|
||||||
|
parserOptions: { tsconfigRootDir: __dirname }
|
||||||
|
};
|
||||||
|
'@
|
||||||
|
|
||||||
|
$eslintConfig | Set-Content -Encoding UTF8 ".eslintrc.js"
|
||||||
|
Success ".eslintrc.js создан."
|
||||||
|
} else {
|
||||||
|
Success ".eslintrc.js уже существует, пропускаем."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 10. .gitignore
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Создаём .gitignore..."
|
||||||
|
|
||||||
|
$gitignore = @'
|
||||||
|
node_modules/
|
||||||
|
lib/
|
||||||
|
dist/
|
||||||
|
temp/
|
||||||
|
*.sppkg
|
||||||
|
.heft/
|
||||||
|
sharepoint/solution/
|
||||||
|
'@
|
||||||
|
|
||||||
|
$gitignore | Set-Content -Encoding UTF8 ".gitignore"
|
||||||
|
Success ".gitignore создан."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 11. Финальный npm install
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
$devDeps = @(
|
||||||
|
"@microsoft/sp-component-base@1.22.1",
|
||||||
|
"@microsoft/sp-core-library@1.22.1",
|
||||||
|
"@microsoft/sp-lodash-subset@1.22.1",
|
||||||
|
"@microsoft/sp-office-ui-fabric-core@1.22.1",
|
||||||
|
"@microsoft/sp-property-pane@1.22.1",
|
||||||
|
"@microsoft/sp-webpart-base@1.22.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
Info "Финальный npm install..."
|
||||||
|
npm install @($devDeps) --save-exact --legacy-peer-deps
|
||||||
|
Success "Все зависимости установлены."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 12. Подключение общей UI-библиотеки @tnsu/ui-kit
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Info "Подключаем @tnsu/ui-kit..."
|
||||||
|
|
||||||
|
# .npmrc: адрес registry для скоупа @tnsu (НЕ секрет, можно коммитить).
|
||||||
|
# Токен на чтение — у каждого разработчика свой, в ~/.npmrc (в проект не кладём).
|
||||||
|
$npmrc = @'
|
||||||
|
@tnsu:registry=http://git.tnsu.kz/api/packages/timur.gorodetskiy/npm/
|
||||||
|
'@
|
||||||
|
$npmrc | Set-Content -Encoding UTF8 ".npmrc"
|
||||||
|
Success ".npmrc создан (адрес registry @tnsu)."
|
||||||
|
|
||||||
|
try {
|
||||||
|
npm install @tnsu/ui-kit
|
||||||
|
Success "@tnsu/ui-kit установлен."
|
||||||
|
Write-Host " Импорт: import { Button } from '@tnsu/ui-kit';" -ForegroundColor DarkGray
|
||||||
|
Write-Host " Стили: import '@tnsu/ui-kit/styles.css'; (один раз в корневом компоненте)" -ForegroundColor DarkGray
|
||||||
|
} catch {
|
||||||
|
Warn "Не удалось поставить @tnsu/ui-kit. Скорее всего нет токена @tnsu в ~/.npmrc."
|
||||||
|
Warn "Создай токен (Gitea → Настройки → Приложения → package: Read) и пропиши в ~/.npmrc:"
|
||||||
|
Warn " //git.tnsu.kz/api/packages/timur.gorodetskiy/npm/:_authToken=ТВОЙ_ТОКЕН"
|
||||||
|
Warn "Потом выполни: npm install @tnsu/ui-kit"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Готово
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=================================================" -ForegroundColor Green
|
||||||
|
Write-Host " Проект успешно инициализирован!" -ForegroundColor Green
|
||||||
|
Write-Host "=================================================" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Доступные команды:" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " npm run serve " -NoNewline -ForegroundColor Cyan
|
||||||
|
Write-Host "— локальная разработка (gulp serve)"
|
||||||
|
Write-Host " npm run build " -NoNewline -ForegroundColor Cyan
|
||||||
|
Write-Host "— сборка через Heft"
|
||||||
|
Write-Host " npm run bundle " -NoNewline -ForegroundColor Cyan
|
||||||
|
Write-Host "— бандлинг для продакшна"
|
||||||
|
Write-Host " npm run package-solution " -NoNewline -ForegroundColor Cyan
|
||||||
|
Write-Host "— создание .sppkg пакета"
|
||||||
|
Write-Host " npm run clean " -NoNewline -ForegroundColor Cyan
|
||||||
|
Write-Host "— очистка артефактов сборки"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " UI-библиотека: " -NoNewline -ForegroundColor Yellow
|
||||||
|
Write-Host "@tnsu/ui-kit подключён (см. .npmrc)" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Следующий шаг: " -NoNewline -ForegroundColor Yellow
|
||||||
|
Write-Host "npm run serve" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
Generated
+20415
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "stroy-bot-monorepo",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.14.0 < 23.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "heft test --clean --production && heft package-solution --production",
|
||||||
|
"start": "heft start --clean",
|
||||||
|
"clean": "heft clean",
|
||||||
|
"eject-webpack": "heft eject-webpack"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fluentui/react": "^8.106.4",
|
||||||
|
"@microsoft/sp-component-base": "1.22.1",
|
||||||
|
"@microsoft/sp-core-library": "1.22.1",
|
||||||
|
"@microsoft/sp-lodash-subset": "1.22.1",
|
||||||
|
"@microsoft/sp-office-ui-fabric-core": "1.22.1",
|
||||||
|
"@microsoft/sp-property-pane": "1.22.1",
|
||||||
|
"@microsoft/sp-webpart-base": "1.22.1",
|
||||||
|
"@tnsu/ui-kit": "^0.12.0",
|
||||||
|
"react": "17.0.1",
|
||||||
|
"react-dom": "17.0.1",
|
||||||
|
"tslib": "2.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@microsoft/eslint-config-spfx": "1.22.2",
|
||||||
|
"@microsoft/eslint-plugin-spfx": "1.22.2",
|
||||||
|
"@microsoft/sp-module-interfaces": "1.22.2",
|
||||||
|
"@microsoft/spfx-heft-plugins": "1.22.2",
|
||||||
|
"@microsoft/spfx-web-build-rig": "1.22.2",
|
||||||
|
"@rushstack/eslint-config": "4.5.2",
|
||||||
|
"@rushstack/heft": "1.1.2",
|
||||||
|
"@types/heft-jest": "1.0.2",
|
||||||
|
"@types/react": "17.0.45",
|
||||||
|
"@types/react-dom": "17.0.17",
|
||||||
|
"@types/webpack-env": "~1.15.2",
|
||||||
|
"@typescript-eslint/parser": "8.46.2",
|
||||||
|
"css-loader": "~7.1.2",
|
||||||
|
"eslint": "8.57.1",
|
||||||
|
"eslint-plugin-react-hooks": "4.3.0",
|
||||||
|
"typescript": "~5.8.0"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"@rushstack/heft": "1.1.2"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@types/react": "17.0.45"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "619a5eba-d596-47e5-a95a-9b9682ed7ec3",
|
||||||
|
"alias": "StroyBotMonorepoWebPart",
|
||||||
|
"componentType": "WebPart",
|
||||||
|
|
||||||
|
// The "*" signifies that the version should be taken from the package.json
|
||||||
|
"version": "*",
|
||||||
|
"manifestVersion": 2,
|
||||||
|
|
||||||
|
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||||
|
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||||
|
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||||
|
"requiresCustomScript": false,
|
||||||
|
"supportedHosts": ["SharePointWebPart", "TeamsPersonalApp", "TeamsTab", "SharePointFullPage"],
|
||||||
|
"supportsThemeVariants": true,
|
||||||
|
|
||||||
|
"preconfiguredEntries": [{
|
||||||
|
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Advanced
|
||||||
|
"group": { "default": "Advanced" },
|
||||||
|
"title": { "default": "stroy-bot-monorepo" },
|
||||||
|
"description": { "default": "stroy-bot-monorepo description" },
|
||||||
|
"officeFabricIconFontName": "Page",
|
||||||
|
"properties": {
|
||||||
|
"description": "stroy-bot-monorepo"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import { Version } from '@microsoft/sp-core-library';
|
||||||
|
import {
|
||||||
|
type IPropertyPaneConfiguration,
|
||||||
|
PropertyPaneTextField
|
||||||
|
} from '@microsoft/sp-property-pane';
|
||||||
|
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||||
|
import { IReadonlyTheme } from '@microsoft/sp-component-base';
|
||||||
|
|
||||||
|
import * as strings from 'StroyBotMonorepoWebPartStrings';
|
||||||
|
import StroyBotMonorepo from './components/StroyBotMonorepo';
|
||||||
|
import { IStroyBotMonorepoProps } from './components/IStroyBotMonorepoProps';
|
||||||
|
|
||||||
|
export interface IStroyBotMonorepoWebPartProps {
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class StroyBotMonorepoWebPart extends BaseClientSideWebPart<IStroyBotMonorepoWebPartProps> {
|
||||||
|
|
||||||
|
private _isDarkTheme: boolean = false;
|
||||||
|
private _environmentMessage: string = '';
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
const element: React.ReactElement<IStroyBotMonorepoProps> = React.createElement(
|
||||||
|
StroyBotMonorepo,
|
||||||
|
{
|
||||||
|
description: this.properties.description,
|
||||||
|
isDarkTheme: this._isDarkTheme,
|
||||||
|
environmentMessage: this._environmentMessage,
|
||||||
|
hasTeamsContext: !!this.context.sdks.microsoftTeams,
|
||||||
|
userDisplayName: this.context.pageContext.user.displayName
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onInit(): Promise<void> {
|
||||||
|
return this._getEnvironmentMessage().then(message => {
|
||||||
|
this._environmentMessage = message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private _getEnvironmentMessage(): Promise<string> {
|
||||||
|
if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook
|
||||||
|
return this.context.sdks.microsoftTeams.teamsJs.app.getContext()
|
||||||
|
.then(context => {
|
||||||
|
let environmentMessage: string = '';
|
||||||
|
switch (context.app.host.name) {
|
||||||
|
case 'Office': // running in Office
|
||||||
|
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
|
||||||
|
break;
|
||||||
|
case 'Outlook': // running in Outlook
|
||||||
|
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
|
||||||
|
break;
|
||||||
|
case 'Teams': // running in Teams
|
||||||
|
case 'TeamsModern':
|
||||||
|
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
environmentMessage = strings.UnknownEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return environmentMessage;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
|
||||||
|
if (!currentTheme) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isDarkTheme = !!currentTheme.isInverted;
|
||||||
|
const {
|
||||||
|
semanticColors
|
||||||
|
} = currentTheme;
|
||||||
|
|
||||||
|
if (semanticColors) {
|
||||||
|
this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null);
|
||||||
|
this.domElement.style.setProperty('--link', semanticColors.link || null);
|
||||||
|
this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onDispose(): void {
|
||||||
|
ReactDom.unmountComponentAtNode(this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: strings.PropertyPaneDescription
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: strings.BasicGroupName,
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('description', {
|
||||||
|
label: strings.DescriptionFieldLabel
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,7 @@
|
|||||||
|
export interface IStroyBotMonorepoProps {
|
||||||
|
description: string;
|
||||||
|
isDarkTheme: boolean;
|
||||||
|
environmentMessage: string;
|
||||||
|
hasTeamsContext: boolean;
|
||||||
|
userDisplayName: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
@import '~@fluentui/react/dist/sass/References.scss';
|
||||||
|
|
||||||
|
.stroyBotMonorepo {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 1em;
|
||||||
|
color: "[theme:bodyText, default: #323130]";
|
||||||
|
color: var(--bodyText);
|
||||||
|
&.teams {
|
||||||
|
font-family: $ms-font-family-fallbacks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcomeImage {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: "[theme:link, default:#03787c]";
|
||||||
|
color: var(--link); // note: CSS Custom Properties support is limited to modern browsers only
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: "[theme:linkHovered, default: #014446]";
|
||||||
|
color: var(--linkHovered); // note: CSS Custom Properties support is limited to modern browsers only
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import styles from './StroyBotMonorepo.module.scss';
|
||||||
|
import type { IStroyBotMonorepoProps } from './IStroyBotMonorepoProps';
|
||||||
|
import { escape } from '@microsoft/sp-lodash-subset';
|
||||||
|
|
||||||
|
export default class StroyBotMonorepo extends React.Component<IStroyBotMonorepoProps> {
|
||||||
|
public render(): React.ReactElement<IStroyBotMonorepoProps> {
|
||||||
|
const {
|
||||||
|
description,
|
||||||
|
isDarkTheme,
|
||||||
|
environmentMessage,
|
||||||
|
hasTeamsContext,
|
||||||
|
userDisplayName
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={`${styles.stroyBotMonorepo} ${hasTeamsContext ? styles.teams : ''}`}>
|
||||||
|
<div className={styles.welcome}>
|
||||||
|
<img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
|
||||||
|
<h2>Well done, {escape(userDisplayName)}!</h2>
|
||||||
|
<div>{environmentMessage}</div>
|
||||||
|
<div>Web part property value: <strong>{escape(description)}</strong></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Welcome to SharePoint Framework!</h3>
|
||||||
|
<p>
|
||||||
|
The SharePoint Framework (SPFx) is a extensibility model for Microsoft Viva, Microsoft Teams and SharePoint. It's the easiest way to extend Microsoft 365 with automatic Single Sign On, automatic hosting and industry standard tooling.
|
||||||
|
</p>
|
||||||
|
<h4>Learn more about SPFx development:</h4>
|
||||||
|
<ul className={styles.links}>
|
||||||
|
<li><a href="https://aka.ms/spfx" target="_blank" rel="noreferrer">SharePoint Framework Overview</a></li>
|
||||||
|
<li><a href="https://aka.ms/spfx-yeoman-graph" target="_blank" rel="noreferrer">Use Microsoft Graph in your solution</a></li>
|
||||||
|
<li><a href="https://aka.ms/spfx-yeoman-teams" target="_blank" rel="noreferrer">Build for Microsoft Teams using SharePoint Framework</a></li>
|
||||||
|
<li><a href="https://aka.ms/spfx-yeoman-viva" target="_blank" rel="noreferrer">Build for Microsoft Viva Connections using SharePoint Framework</a></li>
|
||||||
|
<li><a href="https://aka.ms/spfx-yeoman-store" target="_blank" rel="noreferrer">Publish SharePoint Framework applications to the marketplace</a></li>
|
||||||
|
<li><a href="https://aka.ms/spfx-yeoman-api" target="_blank" rel="noreferrer">SharePoint Framework API reference</a></li>
|
||||||
|
<li><a href="https://aka.ms/m365pnp" target="_blank" rel="noreferrer">Microsoft 365 Developer Community</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "Description",
|
||||||
|
"BasicGroupName": "Group Name",
|
||||||
|
"DescriptionFieldLabel": "Description Field",
|
||||||
|
"AppLocalEnvironmentSharePoint": "The app is running on your local environment as SharePoint web part",
|
||||||
|
"AppLocalEnvironmentTeams": "The app is running on your local environment as Microsoft Teams app",
|
||||||
|
"AppLocalEnvironmentOffice": "The app is running on your local environment in office.com",
|
||||||
|
"AppLocalEnvironmentOutlook": "The app is running on your local environment in Outlook",
|
||||||
|
"AppSharePointEnvironment": "The app is running on SharePoint page",
|
||||||
|
"AppTeamsTabEnvironment": "The app is running in Microsoft Teams",
|
||||||
|
"AppOfficeEnvironment": "The app is running in office.com",
|
||||||
|
"AppOutlookEnvironment": "The app is running in Outlook",
|
||||||
|
"UnknownEnvironment": "The app is running in an unknown environment"
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
declare interface IStroyBotMonorepoWebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
BasicGroupName: string;
|
||||||
|
DescriptionFieldLabel: string;
|
||||||
|
AppLocalEnvironmentSharePoint: string;
|
||||||
|
AppLocalEnvironmentTeams: string;
|
||||||
|
AppLocalEnvironmentOffice: string;
|
||||||
|
AppLocalEnvironmentOutlook: string;
|
||||||
|
AppSharePointEnvironment: string;
|
||||||
|
AppTeamsTabEnvironment: string;
|
||||||
|
AppOfficeEnvironment: string;
|
||||||
|
AppOutlookEnvironment: string;
|
||||||
|
UnknownEnvironment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'StroyBotMonorepoWebPartStrings' {
|
||||||
|
const strings: IStroyBotMonorepoWebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 249 B |
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@microsoft/spfx-web-build-rig/profiles/default/tsconfig-base.json"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user