#はじめに
SaaS型プロダクトで、管理画面側(Shop)とエンドユーザー側(User)との2つがあるプロダクトを開発しています。管理画面側(Shop)は、Shopアカウントごとにログインを行います。
Ruby on RailsとReact(TypeScript)、Material-UIで開発しています。
#やりたいこと
アカウントごとにそれぞれ別の色を設定し、それが各アカウントごとのエンドユーザー側の画面のマスター色に反映されるようにしたい。
例: アカウントA(ShopA)の色は赤色、アカウントB(ShopB)の色は青色。ユーザーはShopAのエンドユーザー画面に入ると赤色がマスター色(ヘッダーやボタンなど)、ShopBのエンドユーザー画面は青色がマスター色。
#方針
- Shop_colorテーブルを追加、ShopテーブルがShop_colorテーブルを1つ持つ(has_one)
- カラーコード(例:#FFFFFF)を一旦追加。
- RailsControllerで、ShopのShop_colorを取得しReact側に送信、Shop_colorがnullであればデフォルトを代入
- React側で受け取ったShop_colorをボタンやヘッダーなどの色に指定する
最初は、CSS変数(カスタムプロパティ)とMaterial-UIのThemeTemplate
で対処しようとしていたのですが、Propsで渡す方が楽なのでpropsで処理することにしました。
#実装
##1. Shop_colorテーブルを追加。
rails g model ShopColor shop_id:string color_code:string
以下のmigrationファイルが生成。
class CreateShopColors < ActiveRecord::Migration[6.0]
def change
create_table :shop_colors do |t|
t.integer :shop_id
t.string :color_code
t.timestamps
end
end
end
モデルではshopにbelongs_to、shop側ではhas_oneさせる。
class ShopColor < ApplicationRecord
belongs_to :shop
end
class Shop < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
省略
has_one :shop_color
end
migrationファイルをあげる。
bundle exec rake db:migrate
##2. カラーコードを一旦追加
カラーコード を一旦手動でDBに登録
rails c
> a = ShopColor.new
> a.shop_id = 1
> a.color_code = "#AAAAAA" #カラーコード
> a.save!
##3. RailsControllerの処理
#省略
def shop_show
@target_shop = Shop.find_by(id: params[:shop_id])
@shop_color = @target_shop.shop_color.color_code ||= "#ABCDEF" #shop_color.color_codeがない場合用にデフォルトのカラーコード を右辺に
end
#省略
##4. React側の処理
<%= react_component("templates/users/user_managements/shop_menu", {
shop: @target_shop,
shopColor: @shop_color,
#省略
}) %>
type Props = {
shop: {
id: number
name: string
}
shopColor: string
}
//クラスで指定する場合
const useStyles = makeStyles<Theme, Props>((theme: Theme) =>
createStyles({
button: {
backgroundColor: (props)=> props.shopColor,
},
})
)
//returnの中で書き込む場合
const ShopShow: React.FC<Props> = (props: Props) => {
const classes = useStyles(props);
return (
<ThemeTemplate>
<Typography style={{color: props.shopColor}}>
Hello
</Typography>
<Button className={classes.button} >
クラス指定のボタン
</Button>
</ThemeTemplate>
)
}
export default ShopShow
ヘッダーなどのAppBarで利用する場合は、呼び出す際にpropsで渡す。Appbar.tsx側でも上記と同様に処理
<AppBarUserComponent isSignedIn={props.isSignedIn} shop={props.shop} shopColor={props.shopColor}/>
#処理の流れ
- UserControllerで
shop_show
メソッドが呼ばれる。@shop_color
にカラーコード を詰める - shop_show.html.erbが呼ばれ、
@shop_color
をshopColor
でreactコンポーネントのshop_menu.tsx
に渡す - shop_menu.tsxでは
Props
でshopColor
の型を用意。 - shop_menu.tsxの
const ShopShow: React.FC<Props> = (props: Props) =>
の最初で、const classes = useStyles(props);
でprops
をuseStyles
に渡す。 - shop_menu.tsxの
useStyles
で、makeStyles<Theme, Props>
でProps
を受け取り、backgroundColor: (props)=> props.shopColor
で色を指定する
#おわりに
ユーザーごとに背景色を変えるなどの処理は良くあるかと思いますので参考になれば嬉しいです。